diff options
| author | lu4nx | 2022-11-25 14:38:29 +0800 |
|---|---|---|
| committer | Eli Zaretskii | 2022-11-27 15:20:49 +0200 |
| commit | d48bb4874bc6cd3e69c7a15fc3c91cc141025c51 (patch) | |
| tree | 21b69d8ab5b9b53034760acff3ece409c1e0388c /lib-src | |
| parent | bacba02e5c7fe69e79c9ba3d65de851f8ccb86bb (diff) | |
| download | emacs-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.c | 149 |
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 | ||
| 376 | static language *get_language_from_langname (const char *); | 376 | static language *get_language_from_langname (const char *); |
| 377 | static void readline (linebuffer *, FILE *); | 377 | static void readline (linebuffer *, FILE *); |
| 378 | static ptrdiff_t readline_internal (linebuffer *, FILE *, char const *); | 378 | static ptrdiff_t readline_internal (linebuffer *, FILE *, char const *, const bool); |
| 379 | static bool nocase_tail (const char *); | 379 | static bool nocase_tail (const char *); |
| 380 | static void get_tag (char *, char **); | 380 | static void get_tag (char *, char **); |
| 381 | static void get_lispy_tag (char *); | 381 | static void get_lispy_tag (char *); |
| @@ -399,7 +399,9 @@ static void free_fdesc (fdesc *); | |||
| 399 | static void pfnote (char *, bool, char *, ptrdiff_t, intmax_t, intmax_t); | 399 | static void pfnote (char *, bool, char *, ptrdiff_t, intmax_t, intmax_t); |
| 400 | static void invalidate_nodes (fdesc *, node **); | 400 | static void invalidate_nodes (fdesc *, node **); |
| 401 | static void put_entries (node *); | 401 | static void put_entries (node *); |
| 402 | static void clean_matched_file_tag (char const * const, char const * const); | ||
| 402 | 403 | ||
| 404 | static void do_move_file (const char *, const char *); | ||
| 403 | static char *concat (const char *, const char *, const char *); | 405 | static char *concat (const char *, const char *, const char *); |
| 404 | static char *skip_spaces (char *); | 406 | static char *skip_spaces (char *); |
| 405 | static char *skip_non_spaces (char *); | 407 | static 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 | */ | ||
| 1444 | static void | ||
| 1445 | clean_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 (®exbuf); | 6915 | linebuffer_init (®exbuf); |
| 6881 | while (readline_internal (®exbuf, regexfp, regexfile) > 0) | 6916 | while (readline_internal (®exbuf, 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 | */ |
| 7240 | static ptrdiff_t | 7277 | static ptrdiff_t |
| 7241 | readline_internal (linebuffer *lbp, FILE *stream, char const *filename) | 7278 | readline_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 | |||
| 7311 | readline (linebuffer *lbp, FILE *stream) | 7348 | readline (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 | ||
| 7709 | static void | ||
| 7710 | do_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). */ |
| 7674 | static char * | 7751 | static char * |