aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src
diff options
context:
space:
mode:
authorlu4nx2022-11-25 14:38:29 +0800
committerEli Zaretskii2022-11-27 15:20:49 +0200
commitd48bb4874bc6cd3e69c7a15fc3c91cc141025c51 (patch)
tree21b69d8ab5b9b53034760acff3ece409c1e0388c /lib-src
parentbacba02e5c7fe69e79c9ba3d65de851f8ccb86bb (diff)
downloademacs-d48bb4874bc6cd3e69c7a15fc3c91cc141025c51.tar.gz
emacs-d48bb4874bc6cd3e69c7a15fc3c91cc141025c51.zip
Fixed ctags local command execute vulnerability
* lib-src/etags.c: (clean_matched_file_tag): New function (do_move_file): New function (readline_internal): Add `leave_cr` parameter, if true, include the \r character * test/manual/etags/CTAGS.good_crlf: New file * test/manual/etags/CTAGS.good_update: New file * test/manual/etags/crlf: New file * test/manual/etags/Makefile: Add `ctags -u` test cases
Diffstat (limited to 'lib-src')
-rw-r--r--lib-src/etags.c149
1 files changed, 113 insertions, 36 deletions
diff --git a/lib-src/etags.c b/lib-src/etags.c
index 3107c7b380e..b6f51dfa83f 100644
--- a/lib-src/etags.c
+++ b/lib-src/etags.c
@@ -375,7 +375,7 @@ static void just_read_file (FILE *);
375 375
376static language *get_language_from_langname (const char *); 376static language *get_language_from_langname (const char *);
377static void readline (linebuffer *, FILE *); 377static void readline (linebuffer *, FILE *);
378static ptrdiff_t readline_internal (linebuffer *, FILE *, char const *); 378static ptrdiff_t readline_internal (linebuffer *, FILE *, char const *, const bool);
379static bool nocase_tail (const char *); 379static bool nocase_tail (const char *);
380static void get_tag (char *, char **); 380static void get_tag (char *, char **);
381static void get_lispy_tag (char *); 381static void get_lispy_tag (char *);
@@ -399,7 +399,9 @@ static void free_fdesc (fdesc *);
399static void pfnote (char *, bool, char *, ptrdiff_t, intmax_t, intmax_t); 399static void pfnote (char *, bool, char *, ptrdiff_t, intmax_t, intmax_t);
400static void invalidate_nodes (fdesc *, node **); 400static void invalidate_nodes (fdesc *, node **);
401static void put_entries (node *); 401static void put_entries (node *);
402static void clean_matched_file_tag (char const * const, char const * const);
402 403
404static void do_move_file (const char *, const char *);
403static char *concat (const char *, const char *, const char *); 405static char *concat (const char *, const char *, const char *);
404static char *skip_spaces (char *); 406static char *skip_spaces (char *);
405static char *skip_non_spaces (char *); 407static char *skip_non_spaces (char *);
@@ -1332,7 +1334,7 @@ main (int argc, char **argv)
1332 if (parsing_stdin) 1334 if (parsing_stdin)
1333 fatal ("cannot parse standard input " 1335 fatal ("cannot parse standard input "
1334 "AND read file names from it"); 1336 "AND read file names from it");
1335 while (readline_internal (&filename_lb, stdin, "-") > 0) 1337 while (readline_internal (&filename_lb, stdin, "-", false) > 0)
1336 process_file_name (filename_lb.buffer, lang); 1338 process_file_name (filename_lb.buffer, lang);
1337 } 1339 }
1338 else 1340 else
@@ -1380,9 +1382,6 @@ main (int argc, char **argv)
1380 /* From here on, we are in (CTAGS && !cxref_style) */ 1382 /* From here on, we are in (CTAGS && !cxref_style) */
1381 if (update) 1383 if (update)
1382 { 1384 {
1383 char *cmd =
1384 xmalloc (strlen (tagfile) + whatlen_max +
1385 sizeof "mv..OTAGS;grep -Fv '\t\t' OTAGS >;rm OTAGS");
1386 for (i = 0; i < current_arg; ++i) 1385 for (i = 0; i < current_arg; ++i)
1387 { 1386 {
1388 switch (argbuffer[i].arg_type) 1387 switch (argbuffer[i].arg_type)
@@ -1393,17 +1392,8 @@ main (int argc, char **argv)
1393 default: 1392 default:
1394 continue; /* the for loop */ 1393 continue; /* the for loop */
1395 } 1394 }
1396 char *z = stpcpy (cmd, "mv "); 1395 clean_matched_file_tag (tagfile, argbuffer[i].what);
1397 z = stpcpy (z, tagfile);
1398 z = stpcpy (z, " OTAGS;grep -Fv '\t");
1399 z = stpcpy (z, argbuffer[i].what);
1400 z = stpcpy (z, "\t' OTAGS >");
1401 z = stpcpy (z, tagfile);
1402 strcpy (z, ";rm OTAGS");
1403 if (system (cmd) != EXIT_SUCCESS)
1404 fatal ("failed to execute shell command");
1405 } 1396 }
1406 free (cmd);
1407 append_to_tagfile = true; 1397 append_to_tagfile = true;
1408 } 1398 }
1409 1399
@@ -1448,6 +1438,51 @@ main (int argc, char **argv)
1448 return EXIT_SUCCESS; 1438 return EXIT_SUCCESS;
1449} 1439}
1450 1440
1441/*
1442 * Equivalent to: mv tags OTAGS;grep -Fv ' filename ' OTAGS >tags;rm OTAGS
1443 */
1444static void
1445clean_matched_file_tag (const char* tagfile, const char* match_file_name)
1446{
1447 FILE *otags_f = fopen ("OTAGS", "wb");
1448 FILE *tag_f = fopen (tagfile, "rb");
1449
1450 if (otags_f == NULL)
1451 pfatal ("OTAGS");
1452
1453 if (tag_f == NULL)
1454 pfatal (tagfile);
1455
1456 int buf_len = strlen (match_file_name) + sizeof ("\t\t ") + 1;
1457 char *buf = xmalloc (buf_len);
1458 snprintf (buf, buf_len, "\t%s\t", match_file_name);
1459
1460 linebuffer line;
1461 linebuffer_init (&line);
1462 while (readline_internal (&line, tag_f, tagfile, true) > 0)
1463 {
1464 if (ferror (tag_f))
1465 pfatal (tagfile);
1466
1467 if (strstr (line.buffer, buf) == NULL)
1468 {
1469 fprintf (otags_f, "%s\n", line.buffer);
1470 if (ferror (tag_f))
1471 pfatal (tagfile);
1472 }
1473 }
1474 free (buf);
1475 free (line.buffer);
1476
1477 if (fclose (otags_f) == EOF)
1478 pfatal ("OTAGS");
1479
1480 if (fclose (tag_f) == EOF)
1481 pfatal (tagfile);
1482
1483 do_move_file ("OTAGS", tagfile);
1484 return;
1485}
1451 1486
1452/* 1487/*
1453 * Return a compressor given the file name. If EXTPTR is non-zero, 1488 * Return a compressor given the file name. If EXTPTR is non-zero,
@@ -1831,7 +1866,7 @@ find_entries (FILE *inf)
1831 1866
1832 /* Else look for sharp-bang as the first two characters. */ 1867 /* Else look for sharp-bang as the first two characters. */
1833 if (parser == NULL 1868 if (parser == NULL
1834 && readline_internal (&lb, inf, infilename) > 0 1869 && readline_internal (&lb, inf, infilename, false) > 0
1835 && lb.len >= 2 1870 && lb.len >= 2
1836 && lb.buffer[0] == '#' 1871 && lb.buffer[0] == '#'
1837 && lb.buffer[1] == '!') 1872 && lb.buffer[1] == '!')
@@ -6878,7 +6913,7 @@ analyze_regex (char *regex_arg)
6878 if (regexfp == NULL) 6913 if (regexfp == NULL)
6879 pfatal (regexfile); 6914 pfatal (regexfile);
6880 linebuffer_init (&regexbuf); 6915 linebuffer_init (&regexbuf);
6881 while (readline_internal (&regexbuf, regexfp, regexfile) > 0) 6916 while (readline_internal (&regexbuf, regexfp, regexfile, false) > 0)
6882 analyze_regex (regexbuf.buffer); 6917 analyze_regex (regexbuf.buffer);
6883 free (regexbuf.buffer); 6918 free (regexbuf.buffer);
6884 if (fclose (regexfp) != 0) 6919 if (fclose (regexfp) != 0)
@@ -7226,11 +7261,13 @@ get_lispy_tag (register char *bp)
7226 7261
7227/* 7262/*
7228 * Read a line of text from `stream' into `lbp', excluding the 7263 * Read a line of text from `stream' into `lbp', excluding the
7229 * newline or CR-NL, if any. Return the number of characters read from 7264 * newline or CR-NL (if `leave_cr` is false), if any. Return the
7230 * `stream', which is the length of the line including the newline. 7265 * number of characters read from `stream', which is the length
7266 * of the line including the newline.
7231 * 7267 *
7232 * On DOS or Windows we do not count the CR character, if any before the 7268 * On DOS or Windows, if `leave_cr` is false, we do not count the
7233 * NL, in the returned length; this mirrors the behavior of Emacs on those 7269 * CR character, if any before the NL, in the returned length;
7270 * this mirrors the behavior of Emacs on those
7234 * platforms (for text files, it translates CR-NL to NL as it reads in the 7271 * platforms (for text files, it translates CR-NL to NL as it reads in the
7235 * file). 7272 * file).
7236 * 7273 *
@@ -7238,7 +7275,7 @@ get_lispy_tag (register char *bp)
7238 * appended to `filebuf'. 7275 * appended to `filebuf'.
7239 */ 7276 */
7240static ptrdiff_t 7277static ptrdiff_t
7241readline_internal (linebuffer *lbp, FILE *stream, char const *filename) 7278readline_internal (linebuffer *lbp, FILE *stream, char const *filename, const bool leave_cr)
7242{ 7279{
7243 char *buffer = lbp->buffer; 7280 char *buffer = lbp->buffer;
7244 char *p = lbp->buffer; 7281 char *p = lbp->buffer;
@@ -7268,19 +7305,19 @@ readline_internal (linebuffer *lbp, FILE *stream, char const *filename)
7268 break; 7305 break;
7269 } 7306 }
7270 if (c == '\n') 7307 if (c == '\n')
7271 { 7308 {
7272 if (p > buffer && p[-1] == '\r') 7309 if (!leave_cr && p > buffer && p[-1] == '\r')
7273 { 7310 {
7274 p -= 1; 7311 p -= 1;
7275 chars_deleted = 2; 7312 chars_deleted = 2;
7276 } 7313 }
7277 else 7314 else
7278 { 7315 {
7279 chars_deleted = 1; 7316 chars_deleted = 1;
7280 } 7317 }
7281 *p = '\0'; 7318 *p = '\0';
7282 break; 7319 break;
7283 } 7320 }
7284 *p++ = c; 7321 *p++ = c;
7285 } 7322 }
7286 lbp->len = p - buffer; 7323 lbp->len = p - buffer;
@@ -7311,7 +7348,7 @@ static void
7311readline (linebuffer *lbp, FILE *stream) 7348readline (linebuffer *lbp, FILE *stream)
7312{ 7349{
7313 linecharno = charno; /* update global char number of line start */ 7350 linecharno = charno; /* update global char number of line start */
7314 ptrdiff_t result = readline_internal (lbp, stream, infilename); 7351 ptrdiff_t result = readline_internal (lbp, stream, infilename, false);
7315 lineno += 1; /* increment global line number */ 7352 lineno += 1; /* increment global line number */
7316 charno += result; /* increment global char number */ 7353 charno += result; /* increment global char number */
7317 7354
@@ -7669,6 +7706,46 @@ etags_mktmp (void)
7669 return templt; 7706 return templt;
7670} 7707}
7671 7708
7709static void
7710do_move_file(const char *src_file, const char *dst_file)
7711{
7712 if (rename (src_file, dst_file) == 0)
7713 return;
7714
7715 FILE *src_f = fopen (src_file, "rb");
7716 FILE *dst_f = fopen (dst_file, "wb");
7717
7718 if (src_f == NULL)
7719 pfatal (src_file);
7720
7721 if (dst_f == NULL)
7722 pfatal (dst_file);
7723
7724 int c;
7725 while ((c = fgetc (src_f)) != EOF)
7726 {
7727 if (ferror (src_f))
7728 pfatal (src_file);
7729
7730 if (ferror (dst_f))
7731 pfatal (dst_file);
7732
7733 if (fputc (c, dst_f) == EOF)
7734 pfatal ("cannot write");
7735 }
7736
7737 if (fclose (src_f) == EOF)
7738 pfatal (src_file);
7739
7740 if (fclose (dst_f) == EOF)
7741 pfatal (dst_file);
7742
7743 if (unlink (src_file) == -1)
7744 pfatal ("unlink error");
7745
7746 return;
7747}
7748
7672/* Return a newly allocated string containing the file name of FILE 7749/* Return a newly allocated string containing the file name of FILE
7673 relative to the absolute directory DIR (which should end with a slash). */ 7750 relative to the absolute directory DIR (which should end with a slash). */
7674static char * 7751static char *