From: "Mark E." To: djgpp-workers AT delorie DOT com Date: Sun, 18 Mar 2001 14:44:05 -0500 MIME-Version: 1.0 Content-type: text/plain; charset=US-ASCII Content-transfer-encoding: 7BIT Subject: Re: zero fill the eof gap 2.1 Message-ID: <3AB4C9B5.25069.91E3E5@localhost> References: <3AB401CC DOT 27501 DOT 4C0CA2 AT localhost> In-reply-to: X-mailer: Pegasus Mail for Win32 (v3.12c) Reply-To: djgpp-workers AT delorie DOT com > Maybe a better option would be to leave _DEV_CDEV out of this, and > instead provide a means to enable and disable the zero-fill on a > by-handle basis? I think that's a good idea. I made changes to do just that. Index: src/docs/kb/wc204.txi =================================================================== RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v retrieving revision 1.57 diff -c -p -r1.57 wc204.txi *** wc204.txi 2001/03/18 16:52:41 1.57 --- wc204.txi 2001/03/18 19:42:22 *************** is disabled. This solves the problem wi *** 348,350 **** --- 348,356 ---- the @env{LFN} variable is set to @samp{n} because @code{stubify.exe} failed to rename the produced executable and reported @code{EINVAL}. + @findex write AT r{, security fix and increased POSIX compliance} + @findex _write AT r{, security fix} + When @code{write} and @code{_write} detect that a file pointer is past EOF, + that space will now be filled with zeroes to increase POSIX compliance + and plug a security hole on disks containing sensitive information. + Index: include/libc/fd_props.h =================================================================== RCS file: /cvs/djgpp/djgpp/include/libc/fd_props.h,v retrieving revision 1.1 diff -c -p -r1.1 fd_props.h *** fd_props.h 2001/03/07 05:34:26 1.1 --- fd_props.h 2001/03/18 19:42:25 *************** extern "C" { *** 13,20 **** #ifndef _POSIX_SOURCE /* Delete file when the last descriptor referencing it is closed. */ ! #define FILE_DESC_TEMPORARY 0x01 typedef struct fd_properties fd_properties; struct fd_properties --- 13,27 ---- #ifndef _POSIX_SOURCE /* Delete file when the last descriptor referencing it is closed. */ ! #define FILE_DESC_TEMPORARY 0x01 + /* Tell write and _write to test for file offset greater than EOF. If so, + they will fill the gap with zeroes. */ + #define FILE_DESC_ZERO_FILL_EOF_GAP 0x02 + + /* Set when there can't be an EOF gap or it should be left alone. */ + #define FILE_DESC_DONT_FILL_EOF_GAP 0x04 + typedef struct fd_properties fd_properties; struct fd_properties *************** void __dup_fd_properties(int _from, int *** 33,40 **** int __clear_fd_properties(int _fd); static __inline__ int __has_fd_properties(int _fd) { ! return __fd_properties && __fd_properties[_fd]; } #endif /* !_POSIX_SOURCE */ --- 40,57 ---- int __clear_fd_properties(int _fd); static __inline__ int __has_fd_properties(int _fd) + { + return _fd >= 0 && __fd_properties && __fd_properties[_fd]; + } + + static __inline__ void __set_fd_flags(int _fd, unsigned long _flags) + { + __fd_properties[_fd]->flags |= _flags; + } + + static __inline__ void __clear_fd_flags(int _fd, unsigned long _flags) { ! __fd_properties[_fd]->flags &= ~_flags; } #endif /* !_POSIX_SOURCE */ Index: src/libc/posix/unistd/lseek.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/unistd/lseek.c,v retrieving revision 1.2 diff -c -p -r1.2 lseek.c *** lseek.c 1998/06/28 17:29:36 1.2 --- lseek.c 2001/03/18 19:42:29 *************** *** 1,3 **** --- 1,4 ---- + /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include *************** *** 7,17 **** --- 8,21 ---- #include #include #include + #include off_t lseek(int handle, off_t offset, int whence) { __dpmi_regs r; + int has_props; + __FSEXT_Function *func = __FSEXT_get_function(handle); if (func) { *************** lseek(int handle, off_t offset, int when *** 30,35 **** --- 34,54 ---- { errno = __doserr_to_errno(r.x.ax); return -1; + } + + has_props = __has_fd_properties(handle); + if (!has_props || + (__fd_properties[handle]->flags & FILE_DESC_DONT_FILL_EOF_GAP) == 0) + { + if (offset > 0) + { + if (!has_props) + has_props = (__set_fd_properties(handle, NULL, 0) == 0); + if (has_props) + __set_fd_flags(handle, FILE_DESC_ZERO_FILL_EOF_GAP); + } + else if (has_props && (whence == SEEK_SET || whence == SEEK_END)) + __clear_fd_flags(handle, FILE_DESC_ZERO_FILL_EOF_GAP); } return (r.x.dx << 16) + r.x.ax; } Index: src/libc/compat/unistd/llseek.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/llseek.c,v retrieving revision 1.1 diff -c -p -r1.1 llseek.c *** llseek.c 2000/06/19 18:00:56 1.1 --- llseek.c 2001/03/18 19:42:32 *************** *** 1,3 **** --- 1,4 ---- + /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ /* * File llseek.c. * *************** *** 14,25 **** #include #include #include - offset_t llseek( int handle, offset_t offset, int whence ) { __dpmi_regs r; __FSEXT_Function *func = __FSEXT_get_function(handle); if( func ) --- 15,27 ---- #include #include #include + #include offset_t llseek( int handle, offset_t offset, int whence ) { __dpmi_regs r; + int has_props; __FSEXT_Function *func = __FSEXT_get_function(handle); if( func ) *************** llseek( int handle, offset_t offset, int *** 42,47 **** --- 44,61 ---- errno = __doserr_to_errno(r.x.ax); return -1; } + + has_props = __has_fd_properties(handle); + if (offset > 0) + { + if (!has_props) + has_props = (__set_fd_properties(handle, NULL, 0) == 0); + if (has_props) + __set_fd_flags(handle, FILE_DESC_ZERO_FILL_EOF_GAP); + } + else if (has_props && (whence == SEEK_SET || whence == SEEK_END)) + __clear_fd_flags(handle, FILE_DESC_ZERO_FILL_EOF_GAP); + return( ( ( (unsigned)r.x.dx ) << 16) + r.x.ax ); } Index: src/libc/posix/unistd/write.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/unistd/write.c,v retrieving revision 1.3 diff -c -p -r1.3 write.c *** write.c 1997/08/31 17:49:14 1.3 --- write.c 2001/03/18 19:42:35 *************** *** 1,3 **** --- 1,4 ---- + /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ *************** *** 12,31 **** #include #include #include #define tblen _go32_info_block.size_of_transfer_buffer int (*__libc_write_termios_hook)(int handle, const void *buffer, size_t count, ssize_t *rv) = NULL; ssize_t write(int handle, const void* buffer, size_t count) { const char *buf = (const char *)buffer; int bytes_in_tb = 0; int offset_into_buf = 0; ! __dpmi_regs r; ! ssize_t rv; __FSEXT_Function *func = __FSEXT_get_function(handle); --- 13,36 ---- #include #include #include + #include #define tblen _go32_info_block.size_of_transfer_buffer int (*__libc_write_termios_hook)(int handle, const void *buffer, size_t count, ssize_t *rv) = NULL; + /* From _write.c. */ + int _write_fill_seek_gap(int fd); + int _write_int(int fd, const char *buffer, size_t count); + ssize_t write(int handle, const void* buffer, size_t count) { const char *buf = (const char *)buffer; int bytes_in_tb = 0; int offset_into_buf = 0; ! int out; ssize_t rv; __FSEXT_Function *func = __FSEXT_get_function(handle); *************** write(int handle, const void* buffer, si *** 45,50 **** --- 50,62 ---- func(__FSEXT_write, &rv, &handle)) /* ... call extension ... */ return rv; /* ... and exit if handled. */ + if (__has_fd_properties(handle) + && (__fd_properties[handle]->flags & FILE_DESC_ZERO_FILL_EOF_GAP)) + { + if (_write_fill_seek_gap(handle) < 0) + return -1; + } + while (offset_into_buf < count) { _farsetsel(_dos_ds); *************** write(int handle, const void* buffer, si *** 62,87 **** offset_into_buf++; } ! /* we now have a transfer buf stuffed with data; write it out */ ! r.x.ax = 0x4000; ! r.x.bx = handle; ! r.x.cx = bytes_in_tb; ! r.x.dx = __tb & 15; ! r.x.ds = __tb / 16; ! __dpmi_int(0x21, &r); ! if (r.x.flags & 1) ! { ! errno = __doserr_to_errno(r.x.ax); return -1; ! } ! ! if (r.x.ax < bytes_in_tb) /* disk full? */ { ! offset_into_buf += r.x.ax; errno = ENOSPC; return count - offset_into_buf; } ! bytes_in_tb = 0; } --- 74,90 ---- offset_into_buf++; } ! /* Write out the contents of the transfer buffer. */ ! out = _write_int(handle, NULL, bytes_in_tb); ! if (out < 0) return -1; ! if (out < bytes_in_tb) { ! offset_into_buf += out; errno = ENOSPC; return count - offset_into_buf; } ! bytes_in_tb = 0; } Index: src/libc/dos/io/_write.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/dos/io/_write.c,v retrieving revision 1.3 diff -c -p -r1.3 _write.c *** _write.c 1996/10/24 23:26:26 1.3 --- _write.c 2001/03/18 19:42:38 *************** *** 10,24 **** #include #include int _write(int handle, const void* buffer, size_t count) { - size_t j, i; - int nput; - unsigned long tbsize; - __dpmi_regs r; - __FSEXT_Function *func = __FSEXT_get_function(handle); if (func) { --- 10,25 ---- #include #include + #include + #include + #include + int _write_fill_seek_gap(int fd); + int _write_int(int fd, const char *buffer, size_t count); + int _write(int handle, const void* buffer, size_t count) { __FSEXT_Function *func = __FSEXT_get_function(handle); if (func) { *************** _write(int handle, const void* buffer, s *** 27,60 **** return rv; } tbsize = _go32_info_block.size_of_transfer_buffer; ! nput = 0; ! do { ! j = (count <= tbsize) ? count : tbsize; ! if (j) ! dosmemput(buffer, j, __tb); r.x.ax = 0x4000; ! r.x.bx = handle; ! r.x.cx = j; r.x.dx = __tb & 15; r.x.ds = __tb / 16; __dpmi_int(0x21, &r); if (r.x.flags & 1) { ! errno = __doserr_to_errno(r.x.ax); return -1; } ! i = r.x.ax; ! count -= i; ! buffer = (void *)((int)buffer + i); ! nput += i; ! } while(count && (i == j)); ! if (count && nput == 0) { errno = ENOSPC; return -1; } ! return nput; } --- 28,154 ---- return rv; } + if (__has_fd_properties(handle) + && (__fd_properties[handle]->flags & FILE_DESC_ZERO_FILL_EOF_GAP)) + { + if (_write_fill_seek_gap(handle) < 0) + return -1; + } + + return _write_int(handle, buffer, count); + } + + /* If the file pointer offset is beyond EOF, fill the gap between EOF and + the file pointer offset with zeroes. This emulates the behavior described + in the POSIX documentation for lseek. */ + int + _write_fill_seek_gap(int fd) + { + offset_t eof_off, cur_off, fill_count; + unsigned long tbsize, buf_size; + unsigned long i; + short fd_info; + + __clear_fd_flags(fd, FILE_DESC_ZERO_FILL_EOF_GAP); + + /* Quit when there can't be an EOF gap or its existance doesn't matter. */ + if (__fd_properties[fd]->flags & FILE_DESC_DONT_FILL_EOF_GAP) + return 0; + + /* Quit when not working with a file. */ + fd_info = _get_dev_info(fd); + if (fd_info & (_DEV_STDIN | _DEV_STDOUT | _DEV_NUL)) + { + /* Don't bother with handles that don't need the fix. */ + __fd_properties[fd]->flags |= FILE_DESC_DONT_FILL_EOF_GAP; + return 0; + } + + /* Quit when unable to get the file length. */ + eof_off = lfilelength (fd); + if (eof_off < 0) + return 0; + + /* Quit when unable to get the current file offset. */ + cur_off = llseek (fd, 0, SEEK_CUR); + if (cur_off < 0) + return 0; + + /* Quit if the current offset is not past EOF. */ + if (cur_off <= eof_off) + return 0; + + /* Quit when unable to seek to EOF. */ + if (llseek (fd, eof_off, SEEK_SET) == -1) + return 0; + + /* Clear once again because the llseek call above will + set the fill test flag. */ + __clear_fd_flags(fd, FILE_DESC_ZERO_FILL_EOF_GAP); + + /* Fill the transfer buffer with zeros. */ tbsize = _go32_info_block.size_of_transfer_buffer; ! fill_count = cur_off - eof_off; ! ! buf_size = (fill_count > tbsize) ? tbsize : fill_count; ! ! i = 0; ! _farsetsel(_dos_ds); ! while (i < buf_size) ! { ! _farnspokel(__tb + i, 0); ! i += 4; ! } ! ! /* Write out 'fill_count' number of zeros. */ ! return _write_int(fd, NULL, fill_count); ! } ! ! /* Write WRITE_COUNT bytes of data to the file associated with FD. ! If BUFFER is not NULL, the data pointed to by BUFFER is put into the ! transfer buffer and written out. Otherwise, the data already in the ! transfer buffer is written out. */ ! int ! _write_int(int fd, const char *buffer, size_t write_count) ! { ! unsigned long buf_size, tb_size; ! unsigned long chunk_count; ! int total_written; ! unsigned short bytes_written; ! __dpmi_regs r; ! ! tb_size = _go32_info_block.size_of_transfer_buffer; ! buf_size = (write_count > tb_size) ? tb_size : write_count; ! ! total_written = 0; ! do ! { ! chunk_count = (write_count <= buf_size) ? write_count : buf_size; ! if (buffer && chunk_count) ! dosmemput(buffer, chunk_count, __tb); r.x.ax = 0x4000; ! r.x.bx = fd; ! r.x.cx = chunk_count; r.x.dx = __tb & 15; r.x.ds = __tb / 16; __dpmi_int(0x21, &r); if (r.x.flags & 1) { ! errno =__doserr_to_errno(r.x.ax); return -1; } ! bytes_written = r.x.ax; ! write_count -= bytes_written; ! total_written += bytes_written; ! if (buffer) ! buffer += bytes_written; ! } while (write_count && (chunk_count == bytes_written)); ! if (write_count && total_written == 0) { errno = ENOSPC; return -1; } ! return total_written; }