From eefb630df4b0da5e6e432f53a5c5aa68bc16d28f Mon Sep 17 00:00:00 2001 From: Ulrich Müller Date: Wed, 30 Nov 2022 11:55:14 +0100 Subject: Fix ctags command execution vulnerability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: https://bugs.gentoo.org/883687 Signed-off-by: Ulrich Müller --- emacs/25.3/04_all_etags.patch | 255 ++++++++++++++++++++++++++++++++++++++++++ emacs/26.3/03_all_etags.patch | 255 ++++++++++++++++++++++++++++++++++++++++++ emacs/27.2/03_all_etags.patch | 255 ++++++++++++++++++++++++++++++++++++++++++ emacs/28.2/02_all_etags.patch | 255 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1020 insertions(+) create mode 100644 emacs/25.3/04_all_etags.patch create mode 100644 emacs/26.3/03_all_etags.patch create mode 100644 emacs/27.2/03_all_etags.patch create mode 100644 emacs/28.2/02_all_etags.patch diff --git a/emacs/25.3/04_all_etags.patch b/emacs/25.3/04_all_etags.patch new file mode 100644 index 0000000..a9f857f --- /dev/null +++ b/emacs/25.3/04_all_etags.patch @@ -0,0 +1,255 @@ +Fix ctags command execution vulnerability (CVE-2022-45939) +Backported from emacs-29 branch +https://bugs.gentoo.org/883687 + +commit d48bb4874bc6cd3e69c7a15fc3c91cc141025c51 +Author: Xi Lu +Date: Fri Nov 25 14:38:29 2022 +0800 + + Fixed ctags local command execute vulnerability + +--- emacs-25.3/lib-src/etags.c ++++ emacs-25.3/lib-src/etags.c +@@ -374,7 +374,7 @@ + + static language *get_language_from_langname (const char *); + static void readline (linebuffer *, FILE *); +-static long readline_internal (linebuffer *, FILE *, char const *); ++static long readline_internal (linebuffer *, FILE *, char const *, const bool); + static bool nocase_tail (const char *); + static void get_tag (char *, char **); + +@@ -396,7 +396,9 @@ + static void pfnote (char *, bool, char *, int, int, long); + static void invalidate_nodes (fdesc *, node **); + static void put_entries (node *); ++static void clean_matched_file_tag (char const * const, char const * const); + ++static void do_move_file (const char *, const char *); + static char *concat (const char *, const char *, const char *); + static char *skip_spaces (char *); + static char *skip_non_spaces (char *); +@@ -1293,7 +1295,7 @@ + if (parsing_stdin) + fatal ("cannot parse standard input " + "AND read file names from it"); +- while (readline_internal (&filename_lb, stdin, "-") > 0) ++ while (readline_internal (&filename_lb, stdin, "-", false) > 0) + process_file_name (filename_lb.buffer, lang); + } + else +@@ -1341,9 +1343,6 @@ + /* From here on, we are in (CTAGS && !cxref_style) */ + if (update) + { +- char *cmd = +- xmalloc (strlen (tagfile) + whatlen_max + +- sizeof "mv..OTAGS;fgrep -v '\t\t' OTAGS >;rm OTAGS"); + for (i = 0; i < current_arg; ++i) + { + switch (argbuffer[i].arg_type) +@@ -1354,17 +1353,8 @@ + default: + continue; /* the for loop */ + } +- char *z = stpcpy (cmd, "mv "); +- z = stpcpy (z, tagfile); +- z = stpcpy (z, " OTAGS;fgrep -v '\t"); +- z = stpcpy (z, argbuffer[i].what); +- z = stpcpy (z, "\t' OTAGS >"); +- z = stpcpy (z, tagfile); +- strcpy (z, ";rm OTAGS"); +- if (system (cmd) != EXIT_SUCCESS) +- fatal ("failed to execute shell command"); ++ clean_matched_file_tag (tagfile, argbuffer[i].what); + } +- free (cmd); + append_to_tagfile = true; + } + +@@ -1393,6 +1383,51 @@ + return EXIT_SUCCESS; + } + ++/* ++ * Equivalent to: mv tags OTAGS;grep -Fv ' filename ' OTAGS >tags;rm OTAGS ++ */ ++static void ++clean_matched_file_tag (const char* tagfile, const char* match_file_name) ++{ ++ FILE *otags_f = fopen ("OTAGS", "wb"); ++ FILE *tag_f = fopen (tagfile, "rb"); ++ ++ if (otags_f == NULL) ++ pfatal ("OTAGS"); ++ ++ if (tag_f == NULL) ++ pfatal (tagfile); ++ ++ int buf_len = strlen (match_file_name) + sizeof ("\t\t ") + 1; ++ char *buf = xmalloc (buf_len); ++ snprintf (buf, buf_len, "\t%s\t", match_file_name); ++ ++ linebuffer line; ++ linebuffer_init (&line); ++ while (readline_internal (&line, tag_f, tagfile, true) > 0) ++ { ++ if (ferror (tag_f)) ++ pfatal (tagfile); ++ ++ if (strstr (line.buffer, buf) == NULL) ++ { ++ fprintf (otags_f, "%s\n", line.buffer); ++ if (ferror (tag_f)) ++ pfatal (tagfile); ++ } ++ } ++ free (buf); ++ free (line.buffer); ++ ++ if (fclose (otags_f) == EOF) ++ pfatal ("OTAGS"); ++ ++ if (fclose (tag_f) == EOF) ++ pfatal (tagfile); ++ ++ do_move_file ("OTAGS", tagfile); ++ return; ++} + + /* + * Return a compressor given the file name. If EXTPTR is non-zero, +@@ -1780,7 +1815,7 @@ + + /* Else look for sharp-bang as the first two characters. */ + if (parser == NULL +- && readline_internal (&lb, inf, infilename) > 0 ++ && readline_internal (&lb, inf, infilename, false) > 0 + && lb.len >= 2 + && lb.buffer[0] == '#' + && lb.buffer[1] == '!') +@@ -6059,7 +6094,7 @@ + if (regexfp == NULL) + pfatal (regexfile); + linebuffer_init (®exbuf); +- while (readline_internal (®exbuf, regexfp, regexfile) > 0) ++ while (readline_internal (®exbuf, regexfp, regexfile, false) > 0) + analyze_regex (regexbuf.buffer); + free (regexbuf.buffer); + if (fclose (regexfp) != 0) +@@ -6391,11 +6426,13 @@ + + /* + * Read a line of text from `stream' into `lbp', excluding the +- * newline or CR-NL, if any. Return the number of characters read from +- * `stream', which is the length of the line including the newline. ++ * newline or CR-NL (if `leave_cr` is false), if any. Return the ++ * number of characters read from `stream', which is the length ++ * of the line including the newline. + * +- * On DOS or Windows we do not count the CR character, if any before the +- * NL, in the returned length; this mirrors the behavior of Emacs on those ++ * On DOS or Windows, if `leave_cr` is false, we do not count the ++ * CR character, if any before the NL, in the returned length; ++ * this mirrors the behavior of Emacs on those + * platforms (for text files, it translates CR-NL to NL as it reads in the + * file). + * +@@ -6403,7 +6440,7 @@ + * appended to `filebuf'. + */ + static long +-readline_internal (linebuffer *lbp, FILE *stream, char const *filename) ++readline_internal (linebuffer *lbp, FILE *stream, char const *filename, const bool leave_cr) + { + char *buffer = lbp->buffer; + char *p = lbp->buffer; +@@ -6433,19 +6470,19 @@ + break; + } + if (c == '\n') +- { +- if (p > buffer && p[-1] == '\r') +- { +- p -= 1; +- chars_deleted = 2; +- } +- else +- { +- chars_deleted = 1; +- } +- *p = '\0'; +- break; +- } ++ { ++ if (!leave_cr && p > buffer && p[-1] == '\r') ++ { ++ p -= 1; ++ chars_deleted = 2; ++ } ++ else ++ { ++ chars_deleted = 1; ++ } ++ *p = '\0'; ++ break; ++ } + *p++ = c; + } + lbp->len = p - buffer; +@@ -6479,7 +6516,7 @@ + long result; + + linecharno = charno; /* update global char number of line start */ +- result = readline_internal (lbp, stream, infilename); /* read line */ ++ result = readline_internal (lbp, stream, infilename, false); /* read line */ + lineno += 1; /* increment global line number */ + charno += result; /* increment global char number */ + +@@ -6839,6 +6876,46 @@ + return templt; + } + ++static void ++do_move_file(const char *src_file, const char *dst_file) ++{ ++ if (rename (src_file, dst_file) == 0) ++ return; ++ ++ FILE *src_f = fopen (src_file, "rb"); ++ FILE *dst_f = fopen (dst_file, "wb"); ++ ++ if (src_f == NULL) ++ pfatal (src_file); ++ ++ if (dst_f == NULL) ++ pfatal (dst_file); ++ ++ int c; ++ while ((c = fgetc (src_f)) != EOF) ++ { ++ if (ferror (src_f)) ++ pfatal (src_file); ++ ++ if (ferror (dst_f)) ++ pfatal (dst_file); ++ ++ if (fputc (c, dst_f) == EOF) ++ pfatal ("cannot write"); ++ } ++ ++ if (fclose (src_f) == EOF) ++ pfatal (src_file); ++ ++ if (fclose (dst_f) == EOF) ++ pfatal (dst_file); ++ ++ if (unlink (src_file) == -1) ++ pfatal ("unlink error"); ++ ++ return; ++} ++ + /* Return a newly allocated string containing the file name of FILE + relative to the absolute directory DIR (which should end with a slash). */ + static char * diff --git a/emacs/26.3/03_all_etags.patch b/emacs/26.3/03_all_etags.patch new file mode 100644 index 0000000..be953c5 --- /dev/null +++ b/emacs/26.3/03_all_etags.patch @@ -0,0 +1,255 @@ +Fix ctags command execution vulnerability (CVE-2022-45939) +Backported from emacs-29 branch +https://bugs.gentoo.org/883687 + +commit d48bb4874bc6cd3e69c7a15fc3c91cc141025c51 +Author: Xi Lu +Date: Fri Nov 25 14:38:29 2022 +0800 + + Fixed ctags local command execute vulnerability + +--- emacs-26.3/lib-src/etags.c ++++ emacs-26.3/lib-src/etags.c +@@ -371,7 +371,7 @@ + + static language *get_language_from_langname (const char *); + static void readline (linebuffer *, FILE *); +-static long readline_internal (linebuffer *, FILE *, char const *); ++static long readline_internal (linebuffer *, FILE *, char const *, const bool); + static bool nocase_tail (const char *); + static void get_tag (char *, char **); + static void get_lispy_tag (char *); +@@ -394,7 +394,9 @@ + static void pfnote (char *, bool, char *, int, int, long); + static void invalidate_nodes (fdesc *, node **); + static void put_entries (node *); ++static void clean_matched_file_tag (char const * const, char const * const); + ++static void do_move_file (const char *, const char *); + static char *concat (const char *, const char *, const char *); + static char *skip_spaces (char *); + static char *skip_non_spaces (char *); +@@ -1307,7 +1309,7 @@ + if (parsing_stdin) + fatal ("cannot parse standard input " + "AND read file names from it"); +- while (readline_internal (&filename_lb, stdin, "-") > 0) ++ while (readline_internal (&filename_lb, stdin, "-", false) > 0) + process_file_name (filename_lb.buffer, lang); + } + else +@@ -1355,9 +1357,6 @@ + /* From here on, we are in (CTAGS && !cxref_style) */ + if (update) + { +- char *cmd = +- xmalloc (strlen (tagfile) + whatlen_max + +- sizeof "mv..OTAGS;grep -Fv '\t\t' OTAGS >;rm OTAGS"); + for (i = 0; i < current_arg; ++i) + { + switch (argbuffer[i].arg_type) +@@ -1368,17 +1367,8 @@ + default: + continue; /* the for loop */ + } +- char *z = stpcpy (cmd, "mv "); +- z = stpcpy (z, tagfile); +- z = stpcpy (z, " OTAGS;grep -Fv '\t"); +- z = stpcpy (z, argbuffer[i].what); +- z = stpcpy (z, "\t' OTAGS >"); +- z = stpcpy (z, tagfile); +- strcpy (z, ";rm OTAGS"); +- if (system (cmd) != EXIT_SUCCESS) +- fatal ("failed to execute shell command"); ++ clean_matched_file_tag (tagfile, argbuffer[i].what); + } +- free (cmd); + append_to_tagfile = true; + } + +@@ -1407,6 +1397,51 @@ + return EXIT_SUCCESS; + } + ++/* ++ * Equivalent to: mv tags OTAGS;grep -Fv ' filename ' OTAGS >tags;rm OTAGS ++ */ ++static void ++clean_matched_file_tag (const char* tagfile, const char* match_file_name) ++{ ++ FILE *otags_f = fopen ("OTAGS", "wb"); ++ FILE *tag_f = fopen (tagfile, "rb"); ++ ++ if (otags_f == NULL) ++ pfatal ("OTAGS"); ++ ++ if (tag_f == NULL) ++ pfatal (tagfile); ++ ++ int buf_len = strlen (match_file_name) + sizeof ("\t\t ") + 1; ++ char *buf = xmalloc (buf_len); ++ snprintf (buf, buf_len, "\t%s\t", match_file_name); ++ ++ linebuffer line; ++ linebuffer_init (&line); ++ while (readline_internal (&line, tag_f, tagfile, true) > 0) ++ { ++ if (ferror (tag_f)) ++ pfatal (tagfile); ++ ++ if (strstr (line.buffer, buf) == NULL) ++ { ++ fprintf (otags_f, "%s\n", line.buffer); ++ if (ferror (tag_f)) ++ pfatal (tagfile); ++ } ++ } ++ free (buf); ++ free (line.buffer); ++ ++ if (fclose (otags_f) == EOF) ++ pfatal ("OTAGS"); ++ ++ if (fclose (tag_f) == EOF) ++ pfatal (tagfile); ++ ++ do_move_file ("OTAGS", tagfile); ++ return; ++} + + /* + * Return a compressor given the file name. If EXTPTR is non-zero, +@@ -1794,7 +1829,7 @@ + + /* Else look for sharp-bang as the first two characters. */ + if (parser == NULL +- && readline_internal (&lb, inf, infilename) > 0 ++ && readline_internal (&lb, inf, infilename, false) > 0 + && lb.len >= 2 + && lb.buffer[0] == '#' + && lb.buffer[1] == '!') +@@ -6293,7 +6328,7 @@ + if (regexfp == NULL) + pfatal (regexfile); + linebuffer_init (®exbuf); +- while (readline_internal (®exbuf, regexfp, regexfile) > 0) ++ while (readline_internal (®exbuf, regexfp, regexfile, false) > 0) + analyze_regex (regexbuf.buffer); + free (regexbuf.buffer); + if (fclose (regexfp) != 0) +@@ -6648,11 +6683,13 @@ + + /* + * Read a line of text from `stream' into `lbp', excluding the +- * newline or CR-NL, if any. Return the number of characters read from +- * `stream', which is the length of the line including the newline. ++ * newline or CR-NL (if `leave_cr` is false), if any. Return the ++ * number of characters read from `stream', which is the length ++ * of the line including the newline. + * +- * On DOS or Windows we do not count the CR character, if any before the +- * NL, in the returned length; this mirrors the behavior of Emacs on those ++ * On DOS or Windows, if `leave_cr` is false, we do not count the ++ * CR character, if any before the NL, in the returned length; ++ * this mirrors the behavior of Emacs on those + * platforms (for text files, it translates CR-NL to NL as it reads in the + * file). + * +@@ -6660,7 +6697,7 @@ + * appended to `filebuf'. + */ + static long +-readline_internal (linebuffer *lbp, FILE *stream, char const *filename) ++readline_internal (linebuffer *lbp, FILE *stream, char const *filename, const bool leave_cr) + { + char *buffer = lbp->buffer; + char *p = lbp->buffer; +@@ -6690,19 +6727,19 @@ + break; + } + if (c == '\n') +- { +- if (p > buffer && p[-1] == '\r') +- { +- p -= 1; +- chars_deleted = 2; +- } +- else +- { +- chars_deleted = 1; +- } +- *p = '\0'; +- break; +- } ++ { ++ if (!leave_cr && p > buffer && p[-1] == '\r') ++ { ++ p -= 1; ++ chars_deleted = 2; ++ } ++ else ++ { ++ chars_deleted = 1; ++ } ++ *p = '\0'; ++ break; ++ } + *p++ = c; + } + lbp->len = p - buffer; +@@ -6736,7 +6773,7 @@ + long result; + + linecharno = charno; /* update global char number of line start */ +- result = readline_internal (lbp, stream, infilename); /* read line */ ++ result = readline_internal (lbp, stream, infilename, false); /* read line */ + lineno += 1; /* increment global line number */ + charno += result; /* increment global char number */ + +@@ -7104,6 +7141,46 @@ + return templt; + } + ++static void ++do_move_file(const char *src_file, const char *dst_file) ++{ ++ if (rename (src_file, dst_file) == 0) ++ return; ++ ++ FILE *src_f = fopen (src_file, "rb"); ++ FILE *dst_f = fopen (dst_file, "wb"); ++ ++ if (src_f == NULL) ++ pfatal (src_file); ++ ++ if (dst_f == NULL) ++ pfatal (dst_file); ++ ++ int c; ++ while ((c = fgetc (src_f)) != EOF) ++ { ++ if (ferror (src_f)) ++ pfatal (src_file); ++ ++ if (ferror (dst_f)) ++ pfatal (dst_file); ++ ++ if (fputc (c, dst_f) == EOF) ++ pfatal ("cannot write"); ++ } ++ ++ if (fclose (src_f) == EOF) ++ pfatal (src_file); ++ ++ if (fclose (dst_f) == EOF) ++ pfatal (dst_file); ++ ++ if (unlink (src_file) == -1) ++ pfatal ("unlink error"); ++ ++ return; ++} ++ + /* Return a newly allocated string containing the file name of FILE + relative to the absolute directory DIR (which should end with a slash). */ + static char * diff --git a/emacs/27.2/03_all_etags.patch b/emacs/27.2/03_all_etags.patch new file mode 100644 index 0000000..1902b03 --- /dev/null +++ b/emacs/27.2/03_all_etags.patch @@ -0,0 +1,255 @@ +Fix ctags command execution vulnerability (CVE-2022-45939) +Backported from emacs-29 branch +https://bugs.gentoo.org/883687 + +commit d48bb4874bc6cd3e69c7a15fc3c91cc141025c51 +Author: Xi Lu +Date: Fri Nov 25 14:38:29 2022 +0800 + + Fixed ctags local command execute vulnerability + +--- emacs-27.2/lib-src/etags.c ++++ emacs-27.2/lib-src/etags.c +@@ -373,7 +373,7 @@ + + static language *get_language_from_langname (const char *); + static void readline (linebuffer *, FILE *); +-static ptrdiff_t readline_internal (linebuffer *, FILE *, char const *); ++static ptrdiff_t readline_internal (linebuffer *, FILE *, char const *, const bool); + static bool nocase_tail (const char *); + static void get_tag (char *, char **); + static void get_lispy_tag (char *); +@@ -396,7 +396,9 @@ + static void pfnote (char *, bool, char *, ptrdiff_t, intmax_t, intmax_t); + static void invalidate_nodes (fdesc *, node **); + static void put_entries (node *); ++static void clean_matched_file_tag (char const * const, char const * const); + ++static void do_move_file (const char *, const char *); + static char *concat (const char *, const char *, const char *); + static char *skip_spaces (char *); + static char *skip_non_spaces (char *); +@@ -1305,7 +1307,7 @@ + if (parsing_stdin) + fatal ("cannot parse standard input " + "AND read file names from it"); +- while (readline_internal (&filename_lb, stdin, "-") > 0) ++ while (readline_internal (&filename_lb, stdin, "-", false) > 0) + process_file_name (filename_lb.buffer, lang); + } + else +@@ -1353,9 +1355,6 @@ + /* From here on, we are in (CTAGS && !cxref_style) */ + if (update) + { +- char *cmd = +- xmalloc (strlen (tagfile) + whatlen_max + +- sizeof "mv..OTAGS;grep -Fv '\t\t' OTAGS >;rm OTAGS"); + for (i = 0; i < current_arg; ++i) + { + switch (argbuffer[i].arg_type) +@@ -1366,17 +1365,8 @@ + default: + continue; /* the for loop */ + } +- char *z = stpcpy (cmd, "mv "); +- z = stpcpy (z, tagfile); +- z = stpcpy (z, " OTAGS;grep -Fv '\t"); +- z = stpcpy (z, argbuffer[i].what); +- z = stpcpy (z, "\t' OTAGS >"); +- z = stpcpy (z, tagfile); +- strcpy (z, ";rm OTAGS"); +- if (system (cmd) != EXIT_SUCCESS) +- fatal ("failed to execute shell command"); ++ clean_matched_file_tag (tagfile, argbuffer[i].what); + } +- free (cmd); + append_to_tagfile = true; + } + +@@ -1405,6 +1395,51 @@ + return EXIT_SUCCESS; + } + ++/* ++ * Equivalent to: mv tags OTAGS;grep -Fv ' filename ' OTAGS >tags;rm OTAGS ++ */ ++static void ++clean_matched_file_tag (const char* tagfile, const char* match_file_name) ++{ ++ FILE *otags_f = fopen ("OTAGS", "wb"); ++ FILE *tag_f = fopen (tagfile, "rb"); ++ ++ if (otags_f == NULL) ++ pfatal ("OTAGS"); ++ ++ if (tag_f == NULL) ++ pfatal (tagfile); ++ ++ int buf_len = strlen (match_file_name) + sizeof ("\t\t ") + 1; ++ char *buf = xmalloc (buf_len); ++ snprintf (buf, buf_len, "\t%s\t", match_file_name); ++ ++ linebuffer line; ++ linebuffer_init (&line); ++ while (readline_internal (&line, tag_f, tagfile, true) > 0) ++ { ++ if (ferror (tag_f)) ++ pfatal (tagfile); ++ ++ if (strstr (line.buffer, buf) == NULL) ++ { ++ fprintf (otags_f, "%s\n", line.buffer); ++ if (ferror (tag_f)) ++ pfatal (tagfile); ++ } ++ } ++ free (buf); ++ free (line.buffer); ++ ++ if (fclose (otags_f) == EOF) ++ pfatal ("OTAGS"); ++ ++ if (fclose (tag_f) == EOF) ++ pfatal (tagfile); ++ ++ do_move_file ("OTAGS", tagfile); ++ return; ++} + + /* + * Return a compressor given the file name. If EXTPTR is non-zero, +@@ -1792,7 +1827,7 @@ + + /* Else look for sharp-bang as the first two characters. */ + if (parser == NULL +- && readline_internal (&lb, inf, infilename) > 0 ++ && readline_internal (&lb, inf, infilename, false) > 0 + && lb.len >= 2 + && lb.buffer[0] == '#' + && lb.buffer[1] == '!') +@@ -6284,7 +6319,7 @@ + if (regexfp == NULL) + pfatal (regexfile); + linebuffer_init (®exbuf); +- while (readline_internal (®exbuf, regexfp, regexfile) > 0) ++ while (readline_internal (®exbuf, regexfp, regexfile, false) > 0) + analyze_regex (regexbuf.buffer); + free (regexbuf.buffer); + if (fclose (regexfp) != 0) +@@ -6638,11 +6673,13 @@ + + /* + * Read a line of text from `stream' into `lbp', excluding the +- * newline or CR-NL, if any. Return the number of characters read from +- * `stream', which is the length of the line including the newline. ++ * newline or CR-NL (if `leave_cr` is false), if any. Return the ++ * number of characters read from `stream', which is the length ++ * of the line including the newline. + * +- * On DOS or Windows we do not count the CR character, if any before the +- * NL, in the returned length; this mirrors the behavior of Emacs on those ++ * On DOS or Windows, if `leave_cr` is false, we do not count the ++ * CR character, if any before the NL, in the returned length; ++ * this mirrors the behavior of Emacs on those + * platforms (for text files, it translates CR-NL to NL as it reads in the + * file). + * +@@ -6650,7 +6687,7 @@ + * appended to `filebuf'. + */ + static ptrdiff_t +-readline_internal (linebuffer *lbp, FILE *stream, char const *filename) ++readline_internal (linebuffer *lbp, FILE *stream, char const *filename, const bool leave_cr) + { + char *buffer = lbp->buffer; + char *p = lbp->buffer; +@@ -6680,19 +6717,19 @@ + break; + } + if (c == '\n') +- { +- if (p > buffer && p[-1] == '\r') +- { +- p -= 1; +- chars_deleted = 2; +- } +- else +- { +- chars_deleted = 1; +- } +- *p = '\0'; +- break; +- } ++ { ++ if (!leave_cr && p > buffer && p[-1] == '\r') ++ { ++ p -= 1; ++ chars_deleted = 2; ++ } ++ else ++ { ++ chars_deleted = 1; ++ } ++ *p = '\0'; ++ break; ++ } + *p++ = c; + } + lbp->len = p - buffer; +@@ -6723,7 +6760,7 @@ + readline (linebuffer *lbp, FILE *stream) + { + linecharno = charno; /* update global char number of line start */ +- ptrdiff_t result = readline_internal (lbp, stream, infilename); ++ ptrdiff_t result = readline_internal (lbp, stream, infilename, false); + lineno += 1; /* increment global line number */ + charno += result; /* increment global char number */ + +@@ -7087,6 +7124,46 @@ + return templt; + } + ++static void ++do_move_file(const char *src_file, const char *dst_file) ++{ ++ if (rename (src_file, dst_file) == 0) ++ return; ++ ++ FILE *src_f = fopen (src_file, "rb"); ++ FILE *dst_f = fopen (dst_file, "wb"); ++ ++ if (src_f == NULL) ++ pfatal (src_file); ++ ++ if (dst_f == NULL) ++ pfatal (dst_file); ++ ++ int c; ++ while ((c = fgetc (src_f)) != EOF) ++ { ++ if (ferror (src_f)) ++ pfatal (src_file); ++ ++ if (ferror (dst_f)) ++ pfatal (dst_file); ++ ++ if (fputc (c, dst_f) == EOF) ++ pfatal ("cannot write"); ++ } ++ ++ if (fclose (src_f) == EOF) ++ pfatal (src_file); ++ ++ if (fclose (dst_f) == EOF) ++ pfatal (dst_file); ++ ++ if (unlink (src_file) == -1) ++ pfatal ("unlink error"); ++ ++ return; ++} ++ + /* Return a newly allocated string containing the file name of FILE + relative to the absolute directory DIR (which should end with a slash). */ + static char * diff --git a/emacs/28.2/02_all_etags.patch b/emacs/28.2/02_all_etags.patch new file mode 100644 index 0000000..d7995c7 --- /dev/null +++ b/emacs/28.2/02_all_etags.patch @@ -0,0 +1,255 @@ +Fix ctags command execution vulnerability (CVE-2022-45939) +Backported from emacs-29 branch +https://bugs.gentoo.org/883687 + +commit d48bb4874bc6cd3e69c7a15fc3c91cc141025c51 +Author: Xi Lu +Date: Fri Nov 25 14:38:29 2022 +0800 + + Fixed ctags local command execute vulnerability + +--- emacs-28.2/lib-src/etags.c ++++ emacs-28.2/lib-src/etags.c +@@ -382,7 +382,7 @@ + + static language *get_language_from_langname (const char *); + static void readline (linebuffer *, FILE *); +-static ptrdiff_t readline_internal (linebuffer *, FILE *, char const *); ++static ptrdiff_t readline_internal (linebuffer *, FILE *, char const *, const bool); + static bool nocase_tail (const char *); + static void get_tag (char *, char **); + static void get_lispy_tag (char *); +@@ -406,7 +406,9 @@ + static void pfnote (char *, bool, char *, ptrdiff_t, intmax_t, intmax_t); + static void invalidate_nodes (fdesc *, node **); + static void put_entries (node *); ++static void clean_matched_file_tag (char const * const, char const * const); + ++static void do_move_file (const char *, const char *); + static char *concat (const char *, const char *, const char *); + static char *skip_spaces (char *); + static char *skip_non_spaces (char *); +@@ -1339,7 +1341,7 @@ + if (parsing_stdin) + fatal ("cannot parse standard input " + "AND read file names from it"); +- while (readline_internal (&filename_lb, stdin, "-") > 0) ++ while (readline_internal (&filename_lb, stdin, "-", false) > 0) + process_file_name (filename_lb.buffer, lang); + } + else +@@ -1387,9 +1389,6 @@ + /* From here on, we are in (CTAGS && !cxref_style) */ + if (update) + { +- char *cmd = +- xmalloc (strlen (tagfile) + whatlen_max + +- sizeof "mv..OTAGS;grep -Fv '\t\t' OTAGS >;rm OTAGS"); + for (i = 0; i < current_arg; ++i) + { + switch (argbuffer[i].arg_type) +@@ -1400,17 +1399,8 @@ + default: + continue; /* the for loop */ + } +- char *z = stpcpy (cmd, "mv "); +- z = stpcpy (z, tagfile); +- z = stpcpy (z, " OTAGS;grep -Fv '\t"); +- z = stpcpy (z, argbuffer[i].what); +- z = stpcpy (z, "\t' OTAGS >"); +- z = stpcpy (z, tagfile); +- strcpy (z, ";rm OTAGS"); +- if (system (cmd) != EXIT_SUCCESS) +- fatal ("failed to execute shell command"); ++ clean_matched_file_tag (tagfile, argbuffer[i].what); + } +- free (cmd); + append_to_tagfile = true; + } + +@@ -1439,6 +1429,51 @@ + return EXIT_SUCCESS; + } + ++/* ++ * Equivalent to: mv tags OTAGS;grep -Fv ' filename ' OTAGS >tags;rm OTAGS ++ */ ++static void ++clean_matched_file_tag (const char* tagfile, const char* match_file_name) ++{ ++ FILE *otags_f = fopen ("OTAGS", "wb"); ++ FILE *tag_f = fopen (tagfile, "rb"); ++ ++ if (otags_f == NULL) ++ pfatal ("OTAGS"); ++ ++ if (tag_f == NULL) ++ pfatal (tagfile); ++ ++ int buf_len = strlen (match_file_name) + sizeof ("\t\t ") + 1; ++ char *buf = xmalloc (buf_len); ++ snprintf (buf, buf_len, "\t%s\t", match_file_name); ++ ++ linebuffer line; ++ linebuffer_init (&line); ++ while (readline_internal (&line, tag_f, tagfile, true) > 0) ++ { ++ if (ferror (tag_f)) ++ pfatal (tagfile); ++ ++ if (strstr (line.buffer, buf) == NULL) ++ { ++ fprintf (otags_f, "%s\n", line.buffer); ++ if (ferror (tag_f)) ++ pfatal (tagfile); ++ } ++ } ++ free (buf); ++ free (line.buffer); ++ ++ if (fclose (otags_f) == EOF) ++ pfatal ("OTAGS"); ++ ++ if (fclose (tag_f) == EOF) ++ pfatal (tagfile); ++ ++ do_move_file ("OTAGS", tagfile); ++ return; ++} + + /* + * Return a compressor given the file name. If EXTPTR is non-zero, +@@ -1822,7 +1857,7 @@ + + /* Else look for sharp-bang as the first two characters. */ + if (parser == NULL +- && readline_internal (&lb, inf, infilename) > 0 ++ && readline_internal (&lb, inf, infilename, false) > 0 + && lb.len >= 2 + && lb.buffer[0] == '#' + && lb.buffer[1] == '!') +@@ -6861,7 +6896,7 @@ + if (regexfp == NULL) + pfatal (regexfile); + linebuffer_init (®exbuf); +- while (readline_internal (®exbuf, regexfp, regexfile) > 0) ++ while (readline_internal (®exbuf, regexfp, regexfile, false) > 0) + analyze_regex (regexbuf.buffer); + free (regexbuf.buffer); + if (fclose (regexfp) != 0) +@@ -7209,11 +7244,13 @@ + + /* + * Read a line of text from `stream' into `lbp', excluding the +- * newline or CR-NL, if any. Return the number of characters read from +- * `stream', which is the length of the line including the newline. ++ * newline or CR-NL (if `leave_cr` is false), if any. Return the ++ * number of characters read from `stream', which is the length ++ * of the line including the newline. + * +- * On DOS or Windows we do not count the CR character, if any before the +- * NL, in the returned length; this mirrors the behavior of Emacs on those ++ * On DOS or Windows, if `leave_cr` is false, we do not count the ++ * CR character, if any before the NL, in the returned length; ++ * this mirrors the behavior of Emacs on those + * platforms (for text files, it translates CR-NL to NL as it reads in the + * file). + * +@@ -7221,7 +7258,7 @@ + * appended to `filebuf'. + */ + static ptrdiff_t +-readline_internal (linebuffer *lbp, FILE *stream, char const *filename) ++readline_internal (linebuffer *lbp, FILE *stream, char const *filename, const bool leave_cr) + { + char *buffer = lbp->buffer; + char *p = lbp->buffer; +@@ -7251,19 +7288,19 @@ + break; + } + if (c == '\n') +- { +- if (p > buffer && p[-1] == '\r') +- { +- p -= 1; +- chars_deleted = 2; +- } +- else +- { +- chars_deleted = 1; +- } +- *p = '\0'; +- break; +- } ++ { ++ if (!leave_cr && p > buffer && p[-1] == '\r') ++ { ++ p -= 1; ++ chars_deleted = 2; ++ } ++ else ++ { ++ chars_deleted = 1; ++ } ++ *p = '\0'; ++ break; ++ } + *p++ = c; + } + lbp->len = p - buffer; +@@ -7294,7 +7331,7 @@ + readline (linebuffer *lbp, FILE *stream) + { + linecharno = charno; /* update global char number of line start */ +- ptrdiff_t result = readline_internal (lbp, stream, infilename); ++ ptrdiff_t result = readline_internal (lbp, stream, infilename, false); + lineno += 1; /* increment global line number */ + charno += result; /* increment global char number */ + +@@ -7652,6 +7689,46 @@ + return templt; + } + ++static void ++do_move_file(const char *src_file, const char *dst_file) ++{ ++ if (rename (src_file, dst_file) == 0) ++ return; ++ ++ FILE *src_f = fopen (src_file, "rb"); ++ FILE *dst_f = fopen (dst_file, "wb"); ++ ++ if (src_f == NULL) ++ pfatal (src_file); ++ ++ if (dst_f == NULL) ++ pfatal (dst_file); ++ ++ int c; ++ while ((c = fgetc (src_f)) != EOF) ++ { ++ if (ferror (src_f)) ++ pfatal (src_file); ++ ++ if (ferror (dst_f)) ++ pfatal (dst_file); ++ ++ if (fputc (c, dst_f) == EOF) ++ pfatal ("cannot write"); ++ } ++ ++ if (fclose (src_f) == EOF) ++ pfatal (src_file); ++ ++ if (fclose (dst_f) == EOF) ++ pfatal (dst_file); ++ ++ if (unlink (src_file) == -1) ++ pfatal ("unlink error"); ++ ++ return; ++} ++ + /* Return a newly allocated string containing the file name of FILE + relative to the absolute directory DIR (which should end with a slash). */ + static char * -- cgit v1.2.3-65-gdbad