diff options
| author | Paul Eggert | 2011-06-19 20:11:40 -0700 |
|---|---|---|
| committer | Paul Eggert | 2011-06-19 20:11:40 -0700 |
| commit | 882f0d8119c9135b06ce9b291a139e4e9c6eeff8 (patch) | |
| tree | d94f839b5aba81b3a9cf78554b054ef5149b63d8 /src | |
| parent | 93f4cf88953806d319e6ab231b4d1332a227d645 (diff) | |
| download | emacs-882f0d8119c9135b06ce9b291a139e4e9c6eeff8.tar.gz emacs-882f0d8119c9135b06ce9b291a139e4e9c6eeff8.zip | |
* filelock.c: Fix some buffer overrun and integer overflow issues.
(get_boot_time): Don't assume that gzip command string fits in 100 bytes.
Reformulate so as not to need the command string.
Invoke gzip -cd rather than gunzip, as it's more portable.
(lock_info_type, lock_file_1, lock_file):
Don't assume pid_t and time_t fit in unsigned long.
(LOCK_PID_MAX): Remove; we now use more-reliable bounds.
(current_lock_owner): Prefer signed type for sizes.
Use memcpy, not strncpy, where memcpy is what is really wanted.
Don't assume (via atoi) that time_t and pid_t fit in int.
Check for time_t and/or pid_t out of range, e.g., via a network share.
Don't alloca where an auto var works fine.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 15 | ||||
| -rw-r--r-- | src/filelock.c | 89 |
2 files changed, 62 insertions, 42 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 3687da81fbb..fd17f85f1e1 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,18 @@ | |||
| 1 | 2011-06-20 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | * filelock.c: Fix some buffer overrun and integer overflow issues. | ||
| 4 | (get_boot_time): Don't assume that gzip command string fits in 100 bytes. | ||
| 5 | Reformulate so as not to need the command string. | ||
| 6 | Invoke gzip -cd rather than gunzip, as it's more portable. | ||
| 7 | (lock_info_type, lock_file_1, lock_file): | ||
| 8 | Don't assume pid_t and time_t fit in unsigned long. | ||
| 9 | (LOCK_PID_MAX): Remove; we now use more-reliable bounds. | ||
| 10 | (current_lock_owner): Prefer signed type for sizes. | ||
| 11 | Use memcpy, not strncpy, where memcpy is what is really wanted. | ||
| 12 | Don't assume (via atoi) that time_t and pid_t fit in int. | ||
| 13 | Check for time_t and/or pid_t out of range, e.g., via a network share. | ||
| 14 | Don't alloca where an auto var works fine. | ||
| 15 | |||
| 1 | 2011-06-19 Paul Eggert <eggert@cs.ucla.edu> | 16 | 2011-06-19 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 17 | ||
| 3 | * fileio.c: Fix some integer overflow issues. | 18 | * fileio.c: Fix some integer overflow issues. |
diff --git a/src/filelock.c b/src/filelock.c index 13b27c72f19..18483b6f3f3 100644 --- a/src/filelock.c +++ b/src/filelock.c | |||
| @@ -168,7 +168,7 @@ get_boot_time (void) | |||
| 168 | /* If we did not find a boot time in wtmp, look at wtmp, and so on. */ | 168 | /* If we did not find a boot time in wtmp, look at wtmp, and so on. */ |
| 169 | for (counter = 0; counter < 20 && ! boot_time; counter++) | 169 | for (counter = 0; counter < 20 && ! boot_time; counter++) |
| 170 | { | 170 | { |
| 171 | char cmd_string[100]; | 171 | char cmd_string[sizeof WTMP_FILE ".19.gz"]; |
| 172 | Lisp_Object tempname, filename; | 172 | Lisp_Object tempname, filename; |
| 173 | int delete_flag = 0; | 173 | int delete_flag = 0; |
| 174 | 174 | ||
| @@ -191,19 +191,16 @@ get_boot_time (void) | |||
| 191 | character long prefix, and call make_temp_file with | 191 | character long prefix, and call make_temp_file with |
| 192 | second arg non-zero, so that it will add not more | 192 | second arg non-zero, so that it will add not more |
| 193 | than 6 characters to the prefix. */ | 193 | than 6 characters to the prefix. */ |
| 194 | tempname = Fexpand_file_name (build_string ("wt"), | 194 | filename = Fexpand_file_name (build_string ("wt"), |
| 195 | Vtemporary_file_directory); | 195 | Vtemporary_file_directory); |
| 196 | tempname = make_temp_name (tempname, 1); | 196 | filename = make_temp_name (filename, 1); |
| 197 | args[0] = Vshell_file_name; | 197 | args[0] = build_string ("gzip"); |
| 198 | args[1] = Qnil; | 198 | args[1] = Qnil; |
| 199 | args[2] = Qnil; | 199 | args[2] = list2 (QCfile, filename); |
| 200 | args[3] = Qnil; | 200 | args[3] = Qnil; |
| 201 | args[4] = build_string ("-c"); | 201 | args[4] = build_string ("-cd"); |
| 202 | sprintf (cmd_string, "gunzip < %s.%d.gz > %s", | 202 | args[5] = tempname; |
| 203 | WTMP_FILE, counter, SDATA (tempname)); | ||
| 204 | args[5] = build_string (cmd_string); | ||
| 205 | Fcall_process (6, args); | 203 | Fcall_process (6, args); |
| 206 | filename = tempname; | ||
| 207 | delete_flag = 1; | 204 | delete_flag = 1; |
| 208 | } | 205 | } |
| 209 | } | 206 | } |
| @@ -284,14 +281,10 @@ typedef struct | |||
| 284 | { | 281 | { |
| 285 | char *user; | 282 | char *user; |
| 286 | char *host; | 283 | char *host; |
| 287 | unsigned long pid; | 284 | pid_t pid; |
| 288 | time_t boot_time; | 285 | time_t boot_time; |
| 289 | } lock_info_type; | 286 | } lock_info_type; |
| 290 | 287 | ||
| 291 | /* When we read the info back, we might need this much more, | ||
| 292 | enough for decimal representation plus null. */ | ||
| 293 | #define LOCK_PID_MAX (4 * sizeof (unsigned long)) | ||
| 294 | |||
| 295 | /* Free the two dynamically-allocated pieces in PTR. */ | 288 | /* Free the two dynamically-allocated pieces in PTR. */ |
| 296 | #define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) | 289 | #define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) |
| 297 | 290 | ||
| @@ -344,7 +337,7 @@ static int | |||
| 344 | lock_file_1 (char *lfname, int force) | 337 | lock_file_1 (char *lfname, int force) |
| 345 | { | 338 | { |
| 346 | register int err; | 339 | register int err; |
| 347 | time_t boot; | 340 | intmax_t boot, pid; |
| 348 | const char *user_name; | 341 | const char *user_name; |
| 349 | const char *host_name; | 342 | const char *host_name; |
| 350 | char *lock_info_str; | 343 | char *lock_info_str; |
| @@ -361,14 +354,16 @@ lock_file_1 (char *lfname, int force) | |||
| 361 | else | 354 | else |
| 362 | host_name = ""; | 355 | host_name = ""; |
| 363 | lock_info_str = (char *)alloca (strlen (user_name) + strlen (host_name) | 356 | lock_info_str = (char *)alloca (strlen (user_name) + strlen (host_name) |
| 364 | + LOCK_PID_MAX + 30); | 357 | + 2 * INT_STRLEN_BOUND (intmax_t) |
| 358 | + sizeof "@.:"); | ||
| 359 | pid = getpid (); | ||
| 365 | 360 | ||
| 366 | if (boot) | 361 | if (boot) |
| 367 | sprintf (lock_info_str, "%s@%s.%lu:%lu", user_name, host_name, | 362 | sprintf (lock_info_str, "%s@%s.%"PRIdMAX":%"PRIdMAX, |
| 368 | (unsigned long) getpid (), (unsigned long) boot); | 363 | user_name, host_name, pid, boot); |
| 369 | else | 364 | else |
| 370 | sprintf (lock_info_str, "%s@%s.%lu", user_name, host_name, | 365 | sprintf (lock_info_str, "%s@%s.%"PRIdMAX, |
| 371 | (unsigned long) getpid ()); | 366 | user_name, host_name, pid); |
| 372 | 367 | ||
| 373 | err = symlink (lock_info_str, lfname); | 368 | err = symlink (lock_info_str, lfname); |
| 374 | if (errno == EEXIST && force) | 369 | if (errno == EEXIST && force) |
| @@ -397,8 +392,9 @@ static int | |||
| 397 | current_lock_owner (lock_info_type *owner, char *lfname) | 392 | current_lock_owner (lock_info_type *owner, char *lfname) |
| 398 | { | 393 | { |
| 399 | int ret; | 394 | int ret; |
| 400 | size_t len; | 395 | ptrdiff_t len; |
| 401 | int local_owner = 0; | 396 | lock_info_type local_owner; |
| 397 | intmax_t n; | ||
| 402 | char *at, *dot, *colon; | 398 | char *at, *dot, *colon; |
| 403 | char readlink_buf[READLINK_BUFSIZE]; | 399 | char readlink_buf[READLINK_BUFSIZE]; |
| 404 | char *lfinfo = emacs_readlink (lfname, readlink_buf); | 400 | char *lfinfo = emacs_readlink (lfname, readlink_buf); |
| @@ -408,12 +404,9 @@ current_lock_owner (lock_info_type *owner, char *lfname) | |||
| 408 | return errno == ENOENT ? 0 : -1; | 404 | return errno == ENOENT ? 0 : -1; |
| 409 | 405 | ||
| 410 | /* Even if the caller doesn't want the owner info, we still have to | 406 | /* Even if the caller doesn't want the owner info, we still have to |
| 411 | read it to determine return value, so allocate it. */ | 407 | read it to determine return value. */ |
| 412 | if (!owner) | 408 | if (!owner) |
| 413 | { | 409 | owner = &local_owner; |
| 414 | owner = (lock_info_type *) alloca (sizeof (lock_info_type)); | ||
| 415 | local_owner = 1; | ||
| 416 | } | ||
| 417 | 410 | ||
| 418 | /* Parse USER@HOST.PID:BOOT_TIME. If can't parse, return -1. */ | 411 | /* Parse USER@HOST.PID:BOOT_TIME. If can't parse, return -1. */ |
| 419 | /* The USER is everything before the last @. */ | 412 | /* The USER is everything before the last @. */ |
| @@ -427,24 +420,34 @@ current_lock_owner (lock_info_type *owner, char *lfname) | |||
| 427 | } | 420 | } |
| 428 | len = at - lfinfo; | 421 | len = at - lfinfo; |
| 429 | owner->user = (char *) xmalloc (len + 1); | 422 | owner->user = (char *) xmalloc (len + 1); |
| 430 | strncpy (owner->user, lfinfo, len); | 423 | memcpy (owner->user, lfinfo, len); |
| 431 | owner->user[len] = 0; | 424 | owner->user[len] = 0; |
| 432 | 425 | ||
| 433 | /* The PID is everything from the last `.' to the `:'. */ | 426 | /* The PID is everything from the last `.' to the `:'. */ |
| 434 | owner->pid = atoi (dot + 1); | 427 | errno = 0; |
| 435 | colon = dot; | 428 | n = strtoimax (dot + 1, NULL, 10); |
| 436 | while (*colon && *colon != ':') | 429 | owner->pid = |
| 437 | colon++; | 430 | ((0 <= n && n <= TYPE_MAXIMUM (pid_t) |
| 431 | && (TYPE_MAXIMUM (pid_t) < INTMAX_MAX || errno != ERANGE)) | ||
| 432 | ? n : 0); | ||
| 433 | |||
| 434 | colon = strchr (dot + 1, ':'); | ||
| 438 | /* After the `:', if there is one, comes the boot time. */ | 435 | /* After the `:', if there is one, comes the boot time. */ |
| 439 | if (*colon == ':') | 436 | n = 0; |
| 440 | owner->boot_time = atoi (colon + 1); | 437 | if (colon) |
| 441 | else | 438 | { |
| 442 | owner->boot_time = 0; | 439 | errno = 0; |
| 440 | n = strtoimax (colon + 1, NULL, 10); | ||
| 441 | } | ||
| 442 | owner->boot_time = | ||
| 443 | ((0 <= n && n <= TYPE_MAXIMUM (time_t) | ||
| 444 | && (TYPE_MAXIMUM (time_t) < INTMAX_MAX || errno != ERANGE)) | ||
| 445 | ? n : 0); | ||
| 443 | 446 | ||
| 444 | /* The host is everything in between. */ | 447 | /* The host is everything in between. */ |
| 445 | len = dot - at - 1; | 448 | len = dot - at - 1; |
| 446 | owner->host = (char *) xmalloc (len + 1); | 449 | owner->host = (char *) xmalloc (len + 1); |
| 447 | strncpy (owner->host, at + 1, len); | 450 | memcpy (owner->host, at + 1, len); |
| 448 | owner->host[len] = 0; | 451 | owner->host[len] = 0; |
| 449 | 452 | ||
| 450 | /* We're done looking at the link info. */ | 453 | /* We're done looking at the link info. */ |
| @@ -476,7 +479,7 @@ current_lock_owner (lock_info_type *owner, char *lfname) | |||
| 476 | } | 479 | } |
| 477 | 480 | ||
| 478 | /* Avoid garbage. */ | 481 | /* Avoid garbage. */ |
| 479 | if (local_owner || ret <= 0) | 482 | if (owner == &local_owner || ret <= 0) |
| 480 | { | 483 | { |
| 481 | FREE_LOCK_INFO (*owner); | 484 | FREE_LOCK_INFO (*owner); |
| 482 | } | 485 | } |
| @@ -539,6 +542,7 @@ lock_file (Lisp_Object fn) | |||
| 539 | register Lisp_Object attack, orig_fn, encoded_fn; | 542 | register Lisp_Object attack, orig_fn, encoded_fn; |
| 540 | register char *lfname, *locker; | 543 | register char *lfname, *locker; |
| 541 | lock_info_type lock_info; | 544 | lock_info_type lock_info; |
| 545 | intmax_t pid; | ||
| 542 | struct gcpro gcpro1; | 546 | struct gcpro gcpro1; |
| 543 | 547 | ||
| 544 | /* Don't do locking while dumping Emacs. | 548 | /* Don't do locking while dumping Emacs. |
| @@ -577,9 +581,10 @@ lock_file (Lisp_Object fn) | |||
| 577 | 581 | ||
| 578 | /* Else consider breaking the lock */ | 582 | /* Else consider breaking the lock */ |
| 579 | locker = (char *) alloca (strlen (lock_info.user) + strlen (lock_info.host) | 583 | locker = (char *) alloca (strlen (lock_info.user) + strlen (lock_info.host) |
| 580 | + LOCK_PID_MAX + 9); | 584 | + INT_STRLEN_BOUND (intmax_t) + sizeof "@ (pid )"); |
| 581 | sprintf (locker, "%s@%s (pid %lu)", lock_info.user, lock_info.host, | 585 | pid = lock_info.pid; |
| 582 | lock_info.pid); | 586 | sprintf (locker, "%s@%s (pid %"PRIdMAX")", |
| 587 | lock_info.user, lock_info.host, pid); | ||
| 583 | FREE_LOCK_INFO (lock_info); | 588 | FREE_LOCK_INFO (lock_info); |
| 584 | 589 | ||
| 585 | attack = call2 (intern ("ask-user-about-lock"), fn, build_string (locker)); | 590 | attack = call2 (intern ("ask-user-about-lock"), fn, build_string (locker)); |