-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Fix Time-of-check time-of-use filesystem race condition and Likely overrunning write #12183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…running write https://github.com/apple/foundationdb/blob/c289917d17fd50307576f0ce6d969b209460c9b7/fdbrpc/HTTP.actor.cpp#L49-L49 Fix the issue should replace the unsafe `sprintf` call with `snprintf`, which allows us to specify the maximum number of characters to write to the buffer, including the null terminator. This ensures that the buffer is not overrun. Specifically: 1. Replace `sprintf(buf, "%%%.02X", c);` with `snprintf(buf, sizeof(buf), "%%%.02X", c);` to limit the write operation to the size of the `buf` array. 2. Ensure that the buffer size is sufficient for the format string and the null terminator. The current size of 4 bytes is adequate for the format `"%%%.02X"`. The program performs a buffer copy or write operation with no upper limit on the size of the copy. By analyzing the bounds of the expressions involved, it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code. ```cpp int sayHello(uint32_t userId) { char buffer[17]; if (userId > 9999) return USER_ID_OUT_OF_BOUNDS; // BAD: this message overflows the buffer if userId >= 1000, // as no space for the null terminator was accounted for sprintf(buffer, "Hello, user %d!", userId); MessageBox(hWnd, buffer, "New Message", MB_OK); return SUCCESS; } ``` In this the call to `sprintf` writes a message of 14 characters (including the terminating null) plus the length of the string conversion of `userId` into a buffer with space for just 17 characters. While `userId` is checked to occupy no more than 4 characters when converted, there is no space in the buffer for the terminating null character if `userId >= 1000`. In this case, the null character overflows the buffer resulting in undefined behavior. To fix this issue these changes should be made: - Control the size of the buffer by declaring it with a compile time constant. - Preferably, replace the call to `sprintf` with `snprintf`, using the defined constant size of the buffer or `sizeof(buffer)` as maximum length to write. This will prevent the buffer overflow. - Increasing the buffer size to account for the full range of `userId` and the terminating null character. #### References [STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator) [STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator](https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator) --- --- https://github.com/apple/foundationdb/blob/c289917d17fd50307576f0ce6d969b209460c9b7/fdbrpc/libeio/eio.c#L2235-L2236 Fix the TOCTOU vulnerability use a safer approach that operates on file descriptors rather than file names wherever possible. For `rmdir`, this can be achieved by opening the directory first and then performing operations on the file descriptor. If the platform does not support such operations, additional measures like locking or verifying the state of the file immediately before the operation can be implemented. The best fix for this issue involves: 1. Using `open` to obtain a file descriptor for the directory. 2. Performing the `rmdir` operation using the file descriptor, ensuring the operation applies to the intended directory. 3. Closing the file descriptor after the operation. The following shows a case where a file is opened and then, if the opening was successful, its permissions are changed with chmod. However, an attacker might change the target of the file name between the initial opening and the permissions change, potentially changing the permissions of a different file. ```c char *file_name; FILE *f_ptr; /* Initialize file_name */ f_ptr = fopen(file_name, "w"); if (f_ptr == NULL) { /* Handle error */ } /* ... */ if (chmod(file_name, S_IRUSR) == -1) { /* Handle error */ } fclose(f_ptr); ``` This can be avoided by using `fchmod` with the file descriptor that was received from opening the file. This ensures that the permissions change is applied to the very same file that was opened. ```c char *file_name; int fd; /* Initialize file_name */ fd = open( file_name, O_WRONLY | O_CREAT | O_EXCL, S_IRWXU ); if (fd == -1) { /* Handle error */ } /* ... */ if (fchmod(fd, S_IRUSR) == -1) { /* Handle error */ } close(fd); ``` #### References [FIO01-C. Be careful using functions that use file names for identification](https://www.securecoding.cert.org/confluence/display/c/FIO01-C.+Be+careful+using+functions+that+use+file+names+for+identification)
Again, thanks for the contribution! For the I have no experience in libeio, so I will leave the change there to more experienced people. |
req->result = -1; | ||
req->errorno = errno; | ||
} else { | ||
req->result = rmdir(path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performing the rmdir operation using the file descriptor, ensuring the operation applies to the intended directory.
I'm confused. The dirfd
is not used?
This doesn't seem to have the TOCTOU problem.
foundationdb/fdbrpc/HTTP.actor.cpp
Line 49 in c289917
Fix the issue should replace the unsafe
sprintf
call withsnprintf
, which allows us to specify the maximum number of characters to write to the buffer, including the null terminator. This ensures that the buffer is not overrun. Specifically:sprintf(buf, "%%%.02X", c);
withsnprintf(buf, sizeof(buf), "%%%.02X", c);
to limit the write operation to the size of thebuf
array."%%%.02X"
.The program performs a buffer copy or write operation with no upper limit on the size of the copy. By analyzing the bounds of the expressions involved, it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.
In this the call to
sprintf
writes a message of 14 characters (including the terminating null) plus the length of the string conversion ofuserId
into a buffer with space for just 17 characters. WhileuserId
is checked to occupy no more than 4 characters when converted, there is no space in the buffer for the terminating null character ifuserId >= 1000
. In this case, the null character overflows the buffer resulting in undefined behavior.To fix this issue these changes should be made:
sprintf
withsnprintf
, using the defined constant size of the buffer orsizeof(buffer)
as maximum length to write. This will prevent the buffer overflow.userId
and the terminating null character.References
STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator
foundationdb/fdbrpc/libeio/eio.c
Lines 2235 to 2236 in c289917
Fix the TOCTOU vulnerability use a safer approach that operates on file descriptors rather than file names wherever possible. For
rmdir
, this can be achieved by opening the directory first and then performing operations on the file descriptor. If the platform does not support such operations, additional measures like locking or verifying the state of the file immediately before the operation can be implemented.The best fix for this issue involves:
open
to obtain a file descriptor for the directory.rmdir
operation using the file descriptor, ensuring the operation applies to the intended directory.The following shows a case where a file is opened and then, if the opening was successful, its permissions are changed with chmod. However, an attacker might change the target of the file name between the initial opening and the permissions change, potentially changing the permissions of a different file.
This can be avoided by using
fchmod
with the file descriptor that was received from opening the file. This ensures that the permissions change is applied to the very same file that was opened.References
FIO01-C. Be careful using functions that use file names for identification
Replace this text with your description here...
Code-Reviewer Section
The general pull request guidelines can be found here.
Please check each of the following things and check all boxes before accepting a PR.
For Release-Branches
If this PR is made against a release-branch, please also check the following:
release-branch
ormain
if this is the youngest branch)