aboutsummaryrefslogtreecommitdiff
path: root/emacs
diff options
context:
space:
mode:
authorUlrich Müller <ulm@gentoo.org>2022-11-30 11:55:14 +0100
committerUlrich Müller <ulm@gentoo.org>2022-11-30 12:17:58 +0100
commiteefb630df4b0da5e6e432f53a5c5aa68bc16d28f (patch)
tree8f1814da1daa79f160c0c10678cf1de71cf300c1 /emacs
parent28.2: Copy patchset from 28.1 (diff)
downloademacs-patches-eefb630df4b0da5e6e432f53a5c5aa68bc16d28f.tar.gz
emacs-patches-eefb630df4b0da5e6e432f53a5c5aa68bc16d28f.tar.bz2
emacs-patches-eefb630df4b0da5e6e432f53a5c5aa68bc16d28f.zip
Bug: https://bugs.gentoo.org/883687 Signed-off-by: Ulrich Müller <ulm@gentoo.org>
Diffstat (limited to 'emacs')
-rw-r--r--emacs/25.3/04_all_etags.patch255
-rw-r--r--emacs/26.3/03_all_etags.patch255
-rw-r--r--emacs/27.2/03_all_etags.patch255
-rw-r--r--emacs/28.2/02_all_etags.patch255
4 files changed, 1020 insertions, 0 deletions
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 <lx@shellcodes.org>
+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 (&regexbuf);
+- while (readline_internal (&regexbuf, regexfp, regexfile) > 0)
++ while (readline_internal (&regexbuf, 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 <lx@shellcodes.org>
+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 (&regexbuf);
+- while (readline_internal (&regexbuf, regexfp, regexfile) > 0)
++ while (readline_internal (&regexbuf, 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 <lx@shellcodes.org>
+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 (&regexbuf);
+- while (readline_internal (&regexbuf, regexfp, regexfile) > 0)
++ while (readline_internal (&regexbuf, 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 <lx@shellcodes.org>
+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 (&regexbuf);
+- while (readline_internal (&regexbuf, regexfp, regexfile) > 0)
++ while (readline_internal (&regexbuf, 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 *