commit 47de1003b9658fb1e1c7ecb4becf0738bb043b70
Author: Dmitry Goncharov <dgoncharov@users.sf.net>
Date:   Sat Feb 25 13:43:08 2023 -0500

    [SV 63840] Prevent match-anything rules from matching default suffixes.
    
    Match-anything rules are not supposed to apply to files with the default
    or user defined suffixes. Ensure that this restriction holds even when
    built-in rules are disabled.
    
    * src/default.c (set_default_suffixes): Define default_suffixes with extern
    linkage.
    (set_default_suffixes): Enter the default suffixes whether the built-in rules
    are enabled or not.  When the built-in rules are disabled, the default
    suffixes are still needed to disqualify match-anything rules.
    * src/read.c (record_files): Reset file->builtin for each user defined suffix.
    This makes a difference when the user defines suffixes that match the default
    suffixes.
    * src/rule.c (convert_default_suffixes): Declare extern default_suffixes.
    (convert_default_suffixes): Convert all default suffixes from array
    default_suffixes to dummy pattern rules.
    (convert_to_pattern): Call convert_default_suffixes.
    (convert_to_pattern): When built-in rules are disabled, convert suffix rules
    only for the suffixes defined in the makefile.
    * tests/scripts/features/matchanyrules: Add new tests.
    * tests/scripts/features/suffixrules: Ditto.

diff --git a/src/default.c b/src/default.c
index e396269b..2dd6c6e7 100644
--- a/src/default.c
+++ b/src/default.c
@@ -36,7 +36,7 @@ this program.  If not, see <https://www.gnu.org/licenses/>.  */
    '.s' must come last, so that a '.o' file will be made from
    a '.c' or '.p' or ... file rather than from a .s file.  */
 
-static char default_suffixes[]
+const char default_suffixes[]
 #ifdef VMS
   /* VMS should include all UNIX/POSIX + some VMS extensions */
   = ".out .exe .a .olb .hlb .tlb .mlb .ln .o .obj .c .cxx .cc .cpp .pas .p \
@@ -683,22 +683,24 @@ static const char *default_variables[] =
 void
 set_default_suffixes (void)
 {
+  struct dep *d;
+  const char *p = default_suffixes;
+
+  /* Enter the default suffixes whether the builtin rules are enabled or not.
+     When the builtin rules are disabled, the default suffixes are still needed to
+     disqualify match-anything rules.  */
+
   suffix_file = enter_file (strcache_add (".SUFFIXES"));
   suffix_file->builtin = 1;
+  suffix_file->deps = enter_prereqs (PARSE_SIMPLE_SEQ ((char **)&p, struct dep),
+                                     NULL);
+  for (d = suffix_file->deps; d; d = d->next)
+    d->file->builtin = 1;
 
   if (no_builtin_rules_flag)
     define_variable_cname ("SUFFIXES", "", o_default, 0);
   else
-    {
-      struct dep *d;
-      const char *p = default_suffixes;
-      suffix_file->deps = enter_prereqs (PARSE_SIMPLE_SEQ ((char **)&p, struct dep),
-                                         NULL);
-      for (d = suffix_file->deps; d; d = d->next)
-        d->file->builtin = 1;
-
-      define_variable_cname ("SUFFIXES", default_suffixes, o_default, 0);
-    }
+    define_variable_cname ("SUFFIXES", default_suffixes, o_default, 0);
 }
 
 /* Enter the default suffix rules as file rules.  This used to be done in
diff --git a/src/read.c b/src/read.c
index b0fc1e1f..651aacf0 100644
--- a/src/read.c
+++ b/src/read.c
@@ -2268,6 +2268,20 @@ record_files (struct nameseq *filenames, int are_also_makes,
       /* Add the dependencies to this file entry.  */
       if (this != 0)
         {
+          if (f == suffix_file)
+            {
+              /* f->deps are the suffixes defined in the makefile.
+                 These suffixes need to be converted to dummy pattern rules.
+                 The default suffixes, were already entered in
+                 set_default_suffixes and have file->builtin == 1.  If these
+                 suffixes match the default suffixes, then need to reset
+                 file->builtin, because convert_to_pattern skips builtin
+                 suffixes.  */
+              struct dep *d;
+              for (d = this; d && d->file; d = d->next)
+                d->file->builtin = 0;
+            }
+
           /* Add the file's old deps and the new ones in THIS together.  */
           if (f->deps == 0)
             f->deps = this;
diff --git a/src/rule.c b/src/rule.c
index d0901f47..23b21bb3 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -284,6 +284,33 @@ convert_suffix_rule (const char *target, const char *source,
   create_pattern_rule (names, percents, 1, 0, deps, cmds, 0);
 }
 
+/* Convert each default suffix to a dummy pattern rule that is just the
+   suffix, with no deps or commands.  This rule exists solely to disqualify
+   match-anything rules.
+   It is necessary to use array default_suffixes, rather than suffix_file, to
+   convert all default suffixes, in case the makefile clears the suffixes.  */
+
+static void
+convert_default_suffixes (void)
+{
+  extern const char default_suffixes[];
+  const char *s = default_suffixes;
+  const char *end = s + strlen (default_suffixes);
+  size_t n;
+  char buf[32];
+  for (; s < end; s += n)
+    {
+      s += strspn (s, " "); /* Skip space.  */
+      n = strcspn (s, " "); /* The length of this suffix.  */
+      assert (s + n <= end);
+      assert (n < 32);
+      memcpy (buf, s, n);
+      buf[n] = '\0';
+      convert_suffix_rule (buf, 0, 0);
+    }
+}
+
+
 /* Convert old-style suffix rules to pattern rules.
    All rules for the suffixes on the .SUFFIXES list are converted and added to
    the chain of pattern rules.  */
@@ -294,6 +321,8 @@ convert_to_pattern (void)
   struct dep *d, *d2;
   char *rulename;
 
+  convert_default_suffixes ();
+
   /* We will compute every potential suffix rule (.x.y) from the list of
      suffixes in the .SUFFIXES target's dependencies and see if it exists.
      First find the longest of the suffixes.  */
@@ -312,10 +341,16 @@ convert_to_pattern (void)
   for (d = suffix_file->deps; d != 0; d = d->next)
     {
       size_t slen;
-
       /* Make a rule that is just the suffix, with no deps or commands.
-         This rule exists solely to disqualify match-anything rules.  */
-      convert_suffix_rule (dep_name (d), 0, 0);
+         This rule exists solely to disqualify match-anything rules.
+         Built-in suffixes were converted in convert_default_suffixes.  */
+      if (d->file->builtin == 0)
+        convert_suffix_rule (dep_name (d), 0, 0);
+
+      /* When built-in rules are disabled, convert suffix rules only for the
+         suffixes defined in the makefile.  */
+      if (d->file->builtin && no_builtin_rules_flag)
+        continue;
 
       if (d->file->cmds != 0)
         /* Record a pattern for this suffix's null-suffix rule.  */
@@ -337,6 +372,12 @@ convert_to_pattern (void)
           if (slen == s2len && streq (dep_name (d), dep_name (d2)))
             continue;
 
+          /* When built-in rules are disabled, convert suffix rules only for
+             the suffixes defined in the makefile.
+             Both suffixes have to be defined.  */
+          if (d2->file->builtin && no_builtin_rules_flag)
+            continue;
+
           memcpy (rulename + slen, dep_name (d2), s2len + 1);
           f = lookup_file (rulename);
 
