View Issue Details

IDProjectCategoryView StatusLast Update
0009295GNUnetutil librarypublic2024-10-23 21:12
Reporterfefe Assigned To 
PrioritynormalSeverityminorReproducibilityhave not tried
Status newResolutionopen 
Summary0009295: GNUNET_DISK_file_test is inherently racy
DescriptionThis function is called, for example, from GNUNET_CONFIGURATION_default.
It will call stat() on the filename and then access. The access man page explicitly warns of using it because that would be a race condition. One of the checks is to make sure it's a regular file.

Here is the code from GNUNET_CONFIGURATION_default:

  2501 /* First, try user configuration. */
  2502 if (NULL != xdg)
  2503 GNUNET_asprintf (&cfgname,
  2504 "%s/%s",
  2505 xdg,
  2506 dpd->config_file);
  2507 else
  2508 cfgname = GNUNET_strdup (dpd->user_config_file);
  2509
  2510 /* If user config doesn't exist, try in
  2511 /etc/<projdir>/<cfgfile> and /etc/<cfgfile> */
  2512 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
  2513 {
  2514 GNUNET_free (cfgname);
  2515 GNUNET_asprintf (&cfgname,
  2516 "/etc/%s",
  2517 dpd->config_file);
  2518 }
  2519 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
  2520 {
  2521 GNUNET_free (cfgname);
  2522 GNUNET_asprintf (&cfgname,
  2523 "/etc/%s/%s",
  2524 dpd->project_dirname,
  2525 dpd->config_file);
  2526 }
  2527 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
  2528 {
  2529 LOG (GNUNET_ERROR_TYPE_ERROR,
  2530 "Unable to top-level configuration file.\n");
  2531 GNUNET_OS_init (pd);
  2532 GNUNET_CONFIGURATION_destroy (cfg);
  2533 GNUNET_free (cfgname);
  2534 return NULL;
  2535 }

Note that this does the same stat test multiple times. Also all the stat tests are worthless if the filesystem changes between the access and the open. Here is the open:

  2537 /* We found a configuration file that looks good, try to load it. */
  2538
  2539 LOG (GNUNET_ERROR_TYPE_DEBUG,
  2540 "Loading top-level configuration from '%s'\n",
  2541 cfgname);
  2542 if (GNUNET_OK !=
  2543 GNUNET_CONFIGURATION_load (cfg, cfgname))

The preferred method is to call open directly and then use fstat to check we opened a regular file.
TagsNo tags attached.

Activities

schanzen

2024-10-23 20:36

administrator   ~0023576

Yeah we obviously call stat way too often. And fstat is missing from the open API.

schanzen

2024-10-23 21:12

administrator   ~0023577

Last edited: 2024-10-23 21:12

Ok I took a jab at this. GNUNET_DISK_file_test is not inherently racy. It is just unreliable when used in combination with open() like this.
It also has other uses.
GNUNET_CONFIGURATION_parse uses GNUNET_DISK_fn_read which does not return a file descriptor and can be used with non-regular files as well (e.g. device files).
So, this would require rewriting GNUNET_CONFIGURATION_parse to use another API.

We may want to check all uses of GNUNET_DISK_fn_read, but I don't think this is a critical bug.
Leaving this open for now.

Issue History

Date Modified Username Field Change
2024-10-23 14:28 fefe New Issue
2024-10-23 20:36 schanzen Note Added: 0023576
2024-10-23 21:12 schanzen Note Added: 0023577
2024-10-23 21:12 schanzen Note Edited: 0023577