I have a real old code from a service that uses named named pipes in message mode (PIPE_TYPE_MESSAGE
) with overlapped i/o (FILE_FLAG_OVERLAPPED
).
The code does the following:
ReadFile
for 4 bytes with overlapped i/o (header + message length). The Client wrote this command with one call toWriteFile
.- After the 4 bytes are read, a
ReadFile
call is made to read the rest of the message (with the known length) without specifying anOVERLAPPED
structure. - After the command is executed the routine continues at stage 1. and awaits the next command.
When I read the documentation
Overlapped operations require a file, named pipe, or communications device that was created with the FILE_FLAG_OVERLAPPED flag. When a thread calls a function (such as the ReadFile function) to perform an overlapped operation, the calling thread must specify a pointer to an OVERLAPPED structure. (If this pointer is NULL, the function return value may incorrectly indicate that the operation completed.)
I have to assume that this code will not work or is at least be called incorrect...
In fact this code is 15 years old and runs on hundreds of machines and it works without problems.
So do I have to tell my boss and colleagues that this code is buggy and it is just luck that it works and this code need to be corrected?
yes, this code is incorrect but can work without errors.
ReadFile
callZwReadFile
. it 5th parameter - IoStatusBlock - Pointer to anIO_STATUS_BLOCK
structure - is mandatory and always must be not 0. for any file. for any I/O type. soReadFile
must pass pointer toIO_STATUS_BLOCK
when it callZwReadFile
. if you pass not 0 pointer toOVERLAPPED
- it pass this pointer asIO_STATUS_BLOCK
toZwReadFile
. the first 2 members ofOVERLAPPED
corresponded toIO_STATUS_BLOCK
. but if you pass 0 in place pointer ofOVERLAPPED
-ReadFile
allocate localIO_STATUS_BLOCK iosb
variable and pass it toZwReadFile
. theIO_STATUS_BLOCK
memory must be valid until I/O not completed. because at the end of I/O kernel write final status to this memory. from another side - if you local variable forIO_STATUS_BLOCK
- it became not valid (point to arbitrary memory in stack) afterReadFile
return. in case synchronous I/O - no problem here, becauseReadFile
not return until I/O not completed. but in case asynchronous I/O - this was UB -ReadFile
can return while I/O still in progress. soIO_STATUS_BLOCK
became not valid before I/O end. and when I/O actually ended - will be overwritten memory at arbitrary place in your thread stack. this can have no any effect or can corrupt your stack. undefinded. depend from where you be (which stack pointer value) at this time