Fix readdir_r with long file names
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): CAHGf_=r9Rz63pho+84ORk0a_oDyJSj-MCnZ56uPrT3L6sVEfeQ@mail.gmail.com
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hello Florian,
Quoted 41 lines · Florian Weimer
Quoted 11 lines · Siddhesh Poyarekar
Review
Quoted 4 lines
(quoted diff) +4
-0+In POSIX.1-2008, @code{readdir} is not thread-safe. In @theglibc{} +implementation, it is safe to call @code{readdir} concurrently on +different @var{dirstream}s (but multiple threads accessing the same +@var{dirstream} result in undefined behavior). @code{readdir_r} is aMinor nit - you could get rid of the round brackets and simply write the line as:
In @theglibc{} implementation, it is safe to call @code{readdir} concurrently on different @var{dirstream}s, but multiple threads accessing the same @var{dirstream} result in undefined behavior.
Applied, thanks.
Quoted 5 lines
Quoted 5 lines
+@item
(quoted diff) +4
-0+POSIX-1.2008 does not guarantee that @code{readdir} is thread-safe, +even when access to the same @var{dirstream} is serialized. But in +current implementations (including @theglibc{}), it is safe to call +@code{readdir} concurrently on different @var{dirstream}s, so there isQuoted 8 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+no requirement to use @code{readdir_r} even in multi-threaded +programs. +This seems to gloss over the fact that one would need synchronization (or
readdir_r) if readdir is called concurrently on the same dirstream. It seems like a bad idea to do this at all, but we probably should add a note about it anyway.I ended up with this:
+@item
(quoted diff) +8
-0+POSIX-1.2008 does not guarantee that @code{readdir} is thread-safe, +even when access to the same @var{dirstream} is serialized. But in +current implementations (including @theglibc{}), it is safe to call +@code{readdir} concurrently on different @var{dirstream}s, so there is +no need to use @code{readdir_r} in most multi-threaded programs. In +the rare case that multiple threads need to read from the same +@var{dirstream}, it is still better to use @code{readdir} and external +synchronization.
Quoted 3 lines
I'm attaching my current version. It compiles, but tests are still running.
I'll update the NEWS file with the bug number on the final commit.
I see that glibc 2.23 deprecates readdir_r(), which prompted me to catch up on this thread. I'd like to see the points you make documented in the readdir_r(3) man page also. Would you be willing to allow that text to be reused / reworked for the page, under that page's existing "verbatim" license (https://www.kernel.org/doc/man-pages/licenses.html#verbatim)?
The text I'd propose to add to the man page would be (new material starting at ===>):
readdir_r()
The readdir_r() function is a reentrant version of readdir(). It reads the next directory entry from the directory stream dirp, and returns it in the caller-allocated buffer pointed to by entry. A pointer to the returned item is placed in *result; if the end of the directory stream was encountered, then NULL is instead returned in *result.
Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede that field within the dirent structure, portable applications that use readdir_r() could allocate the buffer whose address is passed in entry as follows:
name_max = pathconf(dirpath, _PC_NAME_MAX);
if (name_max == -1) /* Limit not defined, or error */
name_max = 255; /* Take a guess */
len = offsetof(struct dirent, d_name) + name_max + 1; entryp = malloc(len);
(POSIX.1 requires that d_name is the last field in a struct dirent.)
===> However, the above approach has problems, and it is recommended
that applications use readdir() instead of readdir_r(). Fur‐ thermore, since version 2.23, glibc deprecates readdir_r(). The reasons are as follows:
* On systems where NAME_MAX is undefined, calling readdir_r() may be unsafe because the interface does not allow the call‐ er to specify the length of the buffer used for the returned directory entry.
* On some systems, readdir_r() can't read directory entries with very long names. When the glibc implementation encoun‐ ters such a name, readdir_r() fails with the error ENAMETOO‐ LONG after the final directory entry has been read. On some other systems, readdir_r() may return a success status, but the returned d_name field may not be null terminated or may be truncated.
* In the current POSIX.1 specification (POSIX.1-2008), read‐ dir_r() is not required to be thread-safe. However, in mod‐ ern implementations (including the glibc implementation), concurrent calls to readdir_r() that specify different directory streams are thread-safe. Therefore, the use of readdir_r() is generally unnecessary in multithreaded pro‐ grams. In cases where multiple threads must read from the same directory stream, using readdir() with external syn‐ chronization is still preferable to the use of readdir_r(), for the reasons given in the points above.
* It is expected that a future version of POSIX.1 will make readdir_r() obsolete, and require that readdir() be thread- safe when concurrently employed on different directory streams.
Cheers,
Michael
===================================
2013-08-15 Florian Weimer <[email protected]>
[BZ #14699]
CVE-2013-4237
* sysdeps/posix/dirstream.h (struct __dirstream): Add errcode member.
* sysdeps/posix/opendir.c (__alloc_dir): Initialize errcode member.
* sysdeps/posix/rewinddir.c (rewinddir): Reset errcode member.
* sysdeps/posix/readdir_r.c (__READDIR_R): Enforce NAME_MAX limit. Return delayed error code. Remove GETDENTS_64BIT_ALIGNED conditional.
* sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c: Do not define GETDENTS_64BIT_ALIGNED.
* sysdeps/unix/sysv/linux/i386/readdir64_r.c: Likewise.
* manual/filesys.texi (Reading/Closing Directory): Document ENAMETOOLONG return value of readdir_r. Recommend readdir more strongly.
* manual/conf.texi (Limits for Files): Add portability note to NAME_MAX, PATH_MAX.
(Pathconf): Add portability note for _PC_NAME_MAX, _PC_PATH_MAX.
manual/conf.texi +9 -0
@@ -1149,6 +1149,9 @@ typed ahead as input. @xref{I/O Queues}. @deftypevr Macro int NAME_MAX The uniform system limit (if any) for the length of a file name component, not including the terminating null character. + +@strong{Portability Note:} On some systems, @theglibc{} defines +@code{NAME_MAX}, but does not actually enforce this limit. @end deftypevr @comment limits.h
@@ -1157,6 +1160,9 @@ including the terminating null character. The uniform system limit (if any) for the length of an entire file name (that is, the argument given to system calls such as @code{open}), including the terminating null character. + +@strong{Portability Note:} @Theglibc{} does not enforce this limit +even if @code{PATH_MAX} is defined. @end deftypevr @cindex limits, pipe buffer size
@@ -1476,6 +1482,9 @@ Inquire about the value of @code{POSIX_REC_MIN_XFER_SIZE}. Inquire about the value of @code{POSIX_REC_XFER_ALIGN}. @end table +@strong{Portability Note:} On some systems, @theglibc{} does not +enforce @code{_PC_NAME_MAX} or @code{_PC_PATH_MAX} limits. + @node Utility Limits @section Utility Program Capacity Limits
manual/filesys.texi +53 -20
96 lines not on this page · raw message
sysdeps/posix/dirstream.h +2 -0
@@ -39,6 +39,8 @@ struct __dirstreamoff_t filepos;/* Position of next entry to read. */ +int errcode;/* Delayed error code. */ +/* Directory block. */ char data[0] __attribute__ ((aligned (__alignof__ (void*)))); };
sysdeps/posix/opendir.c +1 -0
@@ -231,6 +231,7 @@ __alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp) dirp->size =0 ; dirp->offset =0 ; dirp->filepos =0 ; + dirp->errcode =0 ;return dirp; }
sysdeps/posix/readdir_r.c +30 -12
75 lines not on this page · raw message
sysdeps/posix/rewinddir.c +1 -0
@@ -33,6 +33,7 @@ rewinddir (dirp) dirp->filepos =0 ; dirp->offset =0 ; dirp->size =0 ; + dirp->errcode =0 ;#ifndef NOT_IN_libc__libc_lock_unlock (dirp->lock);#endif
sysdeps/unix/sysv/linux/i386/readdir64_r.c +0 -1
@@ -18,7 +18,6 @@#define __READDIR_R __readdir64_r#define __GETDENTS __getdents64#define DIRENT_TYPE struct dirent64 -#define GETDENTS_64BIT_ALIGNED 1#include <sysdeps/posix/readdir_r.c>
sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c +0 -1
@@ -1,5 +1,4 @@#define readdir64_r __no_readdir64_r_decl -#define GETDENTS_64BIT_ALIGNED 1#include <sysdeps/posix/readdir_r.c> #undef readdir64_rweak_alias (__readdir_r, readdir64_r)
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 5 lines · Michael Kerrisk (man-pages)
I see that glibc 2.23 deprecates
readdir_r(), which prompted me to catch up on this thread. I'd like to see the points you make documented in thereaddir_r(3)man page also. Would you be willing to allow that text to be reused / reworked for the page, under that page's existing "verbatim" license (https://www.kernel.org/doc/man-pages/licenses.html#verbatim)?
Hi Michael,
thanks for keeping an eye on deprecations. The deprecation happened for glibc 2.24 (unrelased).
I'm happy to report that I may grant your request.
Quoted 2 lines
The text I'd propose to add to the man page would be (new material starting at ===>):
It may make sense to move this documentation to a separate manual page, specific to readdir_r. This will keep the readdir documentation nice and crisp. Most programmers will never have to consult all these details.
You should remove the example using pathconf because it is not correct.
The kernel does not return valid values for _PC_NAME_MAX and some file
systems (such as CIFS, and CD-ROMs with Joliet extensions once a kernel bug is fixed). The CIFS limit is somewhere around 765, and not 255 as reported by the kernel. If I recall correctly, Windows SMB servers can actually exceed the 255 byte limit. The reason is that Windows NTFS has a limit based on 16-bit UCS-2 characters, and after UTF-8 conversion, the maximum length is more than 255 bytes.
Quoted 18 lines
===> However, the above approach has problems, and it is recommended
that applications usereaddir()instead ofreaddir_r(). Fur‐ thermore, since version 2.23, glibc deprecatesreaddir_r(). The reasons are as follows:* On systems where
NAME_MAXis undefined, callingreaddir_r()may be unsafe because the interface does not allow the call‐ er to specify the length of the buffer used for the returned directory entry.* On some systems,
readdir_r()can't read directory entries with very long names. When the glibc implementation encoun‐ ters such a name,readdir_r()fails with the error ENAMETOO‐ LONG after the final directory entry has been read. On some other systems,readdir_r()may return a success status, but the returnedd_namefield may not be null terminated or may be truncated.
Quoted 5 lines
* In the current POSIX.1 specification (POSIX.1-2008), read‐
dir_r()is not required to be thread-safe. However, in mod‐ ern implementations (including the glibc implementation), concurrent calls toreaddir_r()that specify different directory streams are thread-safe. Therefore, the use of
These two references to readdir_r should be to readdir instead.
I believe there was a historic implementation which implemented fdopendir (fd) as (DIR *) fd, and used a global static buffer for readdir. This is about the only way readdir can be non-thread-safe.
Quoted 10 lines
readdir_r()is generally unnecessary in multithreaded pro‐ grams. In cases where multiple threads must read from the same directory stream, usingreaddir()with external syn‐ chronization is still preferable to the use ofreaddir_r(), for the reasons given in the points above.* It is expected that a future version of POSIX.1 will make
readdir_r()obsolete, and require thatreaddir()be thread- safe when concurrently employed on different directory streams.
Okay.
Thanks,
Florian
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi Florian,
Quoted 8 lines · Florian Weimer
Quoted 5 lines · Michael Kerrisk (man-pages)
I see that glibc 2.23 deprecates
readdir_r(), which prompted me to catch up on this thread. I'd like to see the points you make documented in thereaddir_r(3)man page also. Would you be willing to allow that text to be reused / reworked for the page, under that page's existing "verbatim" license (https://www.kernel.org/doc/man-pages/licenses.html#verbatim)?
Quoted 4 lines
Hi Michael,
thanks for keeping an eye on deprecations. The deprecation happened for glibc 2.24 (unrelased).
Ah yes, I was getting ahead of myself. Fixed that in the page text below.
Quoted 1 line
I'm happy to report that I may grant your request.
Thanks!
Quoted 6 lines
Quoted 2 lines
The text I'd propose to add to the man page would be (new material starting at ===>):
It may make sense to move this documentation to a separate manual page, specific to
readdir_r. This will keep the readdir documentation nice and crisp. Most programmers will never have to consult all these details.
Yes, seems reasonable. Done.
Quoted 1 line
You should remove the example using pathconf because it is not correct.
Done.
Quoted 7 lines
The kernel does not return valid values for
_PC_NAME_MAXand some file
systems (such as CIFS, and CD-ROMs with Joliet extensions once a kernel bug is fixed). The CIFS limit is somewhere around 765, and not 255 as reported by the kernel. If I recall correctly, Windows SMB servers can actually exceed the 255 byte limit. The reason is that Windows NTFS has a limit based on 16-bit UCS-2 characters, and after UTF-8 conversion, the maximum length is more than 255 bytes.
What happens with readdir() when it gets a filename that is larger than 255 characters?
Quoted 4 lines
Quoted 4 lines
===> However, the above approach has problems, and it is recommended
that applications usereaddir()instead ofreaddir_r(). Fur‐ thermore, since version 2.23, glibc deprecatesreaddir_r().
s/23/24/
Quoted 15 lines
Quoted 15 lines
The reasons are as follows:
* On systems where
NAME_MAXis undefined, callingreaddir_r()may be unsafe because the interface does not allow the call‐ er to specify the length of the buffer used for the returned directory entry.* On some systems,
readdir_r()can't read directory entries with very long names. When the glibc implementation encoun‐ ters such a name,readdir_r()fails with the error ENAMETOO‐ LONG after the final directory entry has been read. On some other systems,readdir_r()may return a success status, but the returnedd_namefield may not be null terminated or may be truncated.
Quoted 7 lines
Quoted 5 lines
* In the current POSIX.1 specification (POSIX.1-2008), read‐
dir_r()is not required to be thread-safe. However, in mod‐ ern implementations (including the glibc implementation), concurrent calls toreaddir_r()that specify different directory streams are thread-safe. Therefore, the use ofThese two references to
readdir_rshould be to readdir instead.
Fixed.
Quoted 15 lines
I believe there was a historic implementation which implemented fdopendir (fd) as (DIR *) fd, and used a global static buffer for readdir. This is about the only way readdir can be non-thread-safe.
Quoted 10 lines
readdir_r()is generally unnecessary in multithreaded pro‐ grams. In cases where multiple threads must read from the same directory stream, usingreaddir()with external syn‐ chronization is still preferable to the use ofreaddir_r(), for the reasons given in the points above.* It is expected that a future version of POSIX.1 will make
readdir_r()obsolete, and require thatreaddir()be thread- safe when concurrently employed on different directory streams.
Thanks for all of the feedback Florian! The current versions of the readdir(3) and readdir_r(3) have been pushed to the repo.
Cheers,
Michael
Signature · 7 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/ -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 2 lines · Michael Kerrisk (man-pages)
What happens with
readdir()when it gets a filename that is larger than 255 characters?
Good question. Ugh.
readdir will return a pointer to a struct dirent whose d_name member will not be null-terminated, but the memory following the struct dirent object will contain the rest of the name, and will eventually be null-terminated.
This will work perfectly fine if you don't copy struct dirent objects using memcpy, and the compiler does not optimize things too much. We should implement compiler support for this wart: inhibit optimizations (I think there are already special cases for length-0 and length-1 arrays at the end, so it's not totally without precedent), and warn about sizeof (struct dirent) and using it as a (non-pointer) declarator.
The second part is likely generally useful for structs whose size is
not intended to be part of the ABI.
Florian
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 11 lines · Florian Weimer
Quoted 2 lines · Michael Kerrisk (man-pages)
What happens with
readdir()when it gets a filename that is larger than 255 characters?Good question. Ugh.
readdir will return a pointer to a struct dirent whose
d_namemember will not be null-terminated, but the memory following the struct dirent object will contain the rest of the name, and will eventually be null-terminated.
So, in other words, if the caller users a declaration of the form
struct dirent d;
(rather than say allocating a large buffer dynamically), then we have a buffer overrun?
Cheers,
Michael
Quoted 11 lines
This will work perfectly fine if you don't copy struct dirent objects using memcpy, and the compiler does not optimize things too much. We should implement compiler support for this wart: inhibit optimizations (I think there are already special cases for length-0 and length-1 arrays at the end, so it's not totally without precedent), and warn about sizeof (struct dirent) and using it as a (non-pointer) declarator.
The second part is likely generally useful for structs whose size is
not intended to be part of the ABI.Florian
Signature · 7 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/ -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 13 lines · Michael Kerrisk (man-pages)
Quoted 11 lines · Florian Weimer
Quoted 2 lines · Michael Kerrisk (man-pages)
What happens with
readdir()when it gets a filename that is larger than 255 characters?Good question. Ugh.
readdir will return a pointer to a struct dirent whose
d_namemember will not be null-terminated, but the memory following the struct dirent object will contain the rest of the name, and will eventually be null-terminated.
Quoted 6 lines
So, in other words, if the caller users a declaration of the form
struct dirent d;(rather than say allocating a large buffer dynamically), then we have a buffer overrun?
readdir gives you only a struct dirent * to an internal buffer. If you do
struct dirent *e = readdir (dir); memcpy (&d, e, sizeof (d));
you can end up with a truncated name. According to Paul's comment, this kind of truncation is very visible on Solaris.
Florian
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 21 lines · Florian Weimer
Quoted 13 lines · Michael Kerrisk (man-pages)
Quoted 11 lines · Florian Weimer
Quoted 2 lines · Michael Kerrisk (man-pages)
What happens with
readdir()when it gets a filename that is larger than 255 characters?Good question. Ugh.
readdir will return a pointer to a struct dirent whose
d_namemember will not be null-terminated, but the memory following the struct dirent object will contain the rest of the name, and will eventually be null-terminated.Quoted 6 lines
So, in other words, if the caller users a declaration of the form
struct dirent d;(rather than say allocating a large buffer dynamically), then we have a buffer overrun?
Quoted 7 lines
readdir gives you only a struct dirent * to an internal buffer. If you do
struct dirent *e = readdir (dir); memcpy (&d, e, sizeof (d));you can end up with a truncated name. According to Paul's comment, this kind of truncation is very visible on Solaris.
POSIX also cautions you that this is a permitted definition. See:
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/dirent.h.html
It's covered under the description and rationale.
Rich
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 14 lines · Florian Weimer
Quoted 13 lines · Michael Kerrisk (man-pages)
Quoted 11 lines · Florian Weimer
Quoted 2 lines · Michael Kerrisk (man-pages)
What happens with
readdir()when it gets a filename that is larger than 255 characters?Good question. Ugh.
readdir will return a pointer to a struct dirent whose
d_namemember will not be null-terminated, but the memory following the struct dirent object will contain the rest of the name, and will eventually be null-terminated.
Quoted 8 lines
Quoted 6 lines
So, in other words, if the caller users a declaration of the form
struct dirent d;(rather than say allocating a large buffer dynamically), then we have a buffer overrun?
readdir gives you only a struct dirent * to an internal buffer.
D'oh! Yes, of course. I wasn't thinking clearly as I wrote that last night.
Quoted 6 lines
If you do
struct dirent *e = readdir (dir); memcpy (&d, e, sizeof (d));you can end up with a truncated name.
Got it.
Quoted 2 lines
According to Paul's comment, this
kind of truncation is very visible on Solaris.
Cheers,
Michael
Signature · 7 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/ -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 5 lines · Florian Weimer
We
should implement compiler support for this wart: inhibit optimizations (I think there are already special cases for length-0 and length-1 arrays at the end, so it's not totally without precedent), and warn about sizeof (struct dirent) and using it as a (non-pointer) declarator.
Why not use a flexible array member for this? Sure, that assumes C99, but flexible array members are pretty much universally supported now (and we can fall back on the current layout for pre-C99 compilers). This would work better with modern compilers that treat small arrays with more respect than traditional C compilers did. And as I understand things, it would conform to POSIX (and if I'm wrong, POSIX should get fixed....).
For what it's worth, portable code cannot copy struct dirent values anyway, as this loses file names in operating systems like Solaris where d_name has size 1.
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 8 lines · Paul Eggert
Quoted 5 lines · Florian Weimer
We
should implement compiler support for this wart: inhibit optimizations (I think there are already special cases for length-0 and length-1 arrays at the end, so it's not totally without precedent), and warn about sizeof (struct dirent) and using it as a (non-pointer) declarator.Why not use a flexible array member for this?
For which part, and how exactly?
You can't put a flexible array member into a transparent union.
Quoted 7 lines
Sure, that assumes C99,
but flexible array members are pretty much universally supported now (and we can fall back on the current layout for pre-C99 compilers). This would work better with modern compilers that treat small arrays with more respect than traditional C compilers did. And as I understand things, it would conform to POSIX (and if I'm wrong, POSIX should get fixed....).
If you mean to add some zero-width padding member at the end of the struct, after the d_name member, then I'm worried that makes overrunning the d_name array member even more undefined than it already is. :)
Quoted 3 lines
For what it's worth, portable code cannot copy struct dirent values anyway, as this loses file names in operating systems like Solaris where
d_namehas size 1.
Interesting. I feel slightly better now about having this overrunning d_name member.
Florian
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 2 lines · Florian Weimer
Quoted 1 line
Why not use a flexible array member for this?
For which part, and how exactly?
Something like the attached patch, say. (Totally untested.)
Quoted 1 line
You can't put a flexible array member into a transparent union.
That's OK. Any such usage of struct dirent would be unportable anyway.
Quoted 3 lines
If you mean to add some zero-width padding member at the end of the struct, after the
d_namemember, then I'm worried that makes overrunning thed_namearray member even more undefined than it already is.
No, no padding member, just use C99 the way it was designed. This should improve overrun detection in programs like valgrind. With glibc's current definition these programs can be fooled into thinking that struct dirent accesses are invalid (outside of array bounds) when they are actually OK, so people shut off array-bounds checking. If we used flexible array members, valgrind etc. should know that the array's upper bound is unknown and should not issue so many false alarms, so people can leave bounds checking on.
Also, I expect this sort of thing will become more important as GCC -fbounds-check becomes more practical.
If flexible arrays are no-go for some reason, I suppose we could use 'char 'd_name[SIZE_MAX - 1000];' instead. That should get peoples' attention. :-)
bits/dirent.h +2 -3
@@ -32,7 +32,7 @@ struct direntunsigned char d_namlen;/* Length of the file name. */ /* Only this member is in the POSIX standard. */ -char d_name[1 ];/* File name (actually longer). */ + char d_name __flexarr; /* File name. */ };#ifdef __USE_LARGEFILE64
@@ -42,8 +42,7 @@ struct dirent64unsigned short int d_reclen;unsigned char d_type;unsigned char d_namlen; - - char d_name[1]; + char d_name __flexarr; /* File name. */ };#endif
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 14 lines · Paul Eggert
Quoted 2 lines · Florian Weimer
Quoted 1 line
Why not use a flexible array member for this?
For which part, and how exactly?
Something like the attached patch, say. (Totally untested.)
Quoted 1 line
You can't put a flexible array member into a transparent union.
That's OK. Any such usage of struct dirent would be unportable anyway.
Quoted 3 lines
If you mean to add some zero-width padding member at the end of the struct, after the
d_namemember, then I'm worried that makes overrunning thed_namearray member even more undefined than it already is.
Quoted 8 lines
No, no padding member, just use C99 the way it was designed. This should improve overrun detection in programs like valgrind. With glibc's current definition these programs can be fooled into thinking that struct dirent accesses are invalid (outside of array bounds) when they are actually OK, so people shut off array-bounds checking. If we used flexible array members, valgrind etc. should know that the array's upper bound is unknown and should not issue so many false alarms, so people can leave bounds checking on.
I don't think valgrind can see the difference, but you are correct in principle (this is essentially the “undefined” part I was worried about).
Unfortunately, GCC does not produce a warning for taking the size of a struct with a flexible member, or for using it in a non-pointer declarator, so it does only half of what we want. And at the cost of changing sizeof (struct dirent), which can't be a good thing.
Quoted 3 lines
If flexible arrays are no-go for some reason, I suppose we could use 'char 'd_name[SIZE_MAX - 1000];' instead. That should get peoples' attention. :-)
GCC refuses to compile the type definition, not just declarations. Refusing declarations with an error would break quite a lot of existing configure tests.
struct dirent d; int z; z - d.d_ino;
is a common idiom to check for struct members.
Florian
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 3 lines · Florian Weimer
GCC does not produce a warning for taking the size of a struct with a flexible member, or for using it in a non-pointer declarator, so it does only half of what we want.
Ouch. That's annoying, but I guess C11 requires it. At least we get half a loaf....
Quoted 2 lines
And at the cost of
changing sizeof (struct dirent), which can't be a good thing.
Any program that depends on sizeof (struct dirent) is broken already, so this isn't that worrisome.
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 5 lines · Paul Eggert
Quoted 2 lines
And at the cost of
changing sizeof (struct dirent), which can't be a good thing.Any program that depends on sizeof (struct dirent) is broken already, so this isn't that worrisome.
Just to be clear, you looked at the wrong struct dirent definition for GNU/Linux, there is a sysdeps override.
Right now, most programs relying on sizeof (struct dirent) work well in almost all cases. We really don't want to break that. There appears to be an overlap between these programs and users of readdir_r, so once we remove that from the API, we should have better story for struct dirent declarators as well.
Florian
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 8 lines · Florian Weimer
Quoted 5 lines · Paul Eggert
Quoted 2 lines
And at the cost of
changing sizeof (struct dirent), which can't be a good thing.Any program that depends on sizeof (struct dirent) is broken already, so this isn't that worrisome.
Quoted 8 lines
Just to be clear, you looked at the wrong struct dirent definition for GNU/Linux, there is a sysdeps override.
Right now, most programs relying on sizeof (struct dirent) work well in almost all cases. We really don't want to break that. There appears to be an overlap between these programs and users of
readdir_r, so once we remove that from the API, we should have better story for struct dirent declarators as well.
So, it seems like much more could be said about this in documentation. How about the following text for the man page?
DESCRIPTION
[...]
In the glibc implementation, the dirent structure is defined as follows:
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
[...]
NOTES
The d_name field
The dirent structure definition shown above is taken from the glibc headers, and shows the d_name field with a fixed size.
Warning: applications should avoid any dependence on the size of the dname field. POSIX defines it as char d_name[], a char‐ acter array of unspecified size, with at most NAME_MAX charac‐ ters preceding the terminating null byte ('\0').
POSIX.1 explicitly notes that this field should not be used as
an lvalue. The standard also notes that the use of
sizeof(d_name) (and by implication sizeof(struct dirent)) is incorrect; use strlen(d_name) instead. (On some systems, this field is defined as char d_name[1]!)
Note that while the call
fpathconf(fd, _PC_NAME_MAX)
returns the value 255 for most filesystems, on some filesystems (e.g., CIFS, Windows SMB servers), the null-terminated filename that is (correctly) returned in d_name can actually exceed this size. (In such cases, the d_reclen field will contain a value that exceeds the size of the glibc dirent structure shown above.)
Cheers,
Michael
Signature · 7 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/ -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
Ping on this man page text. Anyone have an opinion for or against?
Thanks,
Michael
Quoted 69 lines · Michael Kerrisk (man-pages)
Quoted 8 lines · Florian Weimer
Quoted 5 lines · Paul Eggert
Quoted 2 lines
And at the cost of
changing sizeof (struct dirent), which can't be a good thing.Any program that depends on sizeof (struct dirent) is broken already, so this isn't that worrisome.
Quoted 8 lines
Just to be clear, you looked at the wrong struct dirent definition for GNU/Linux, there is a sysdeps override.
Right now, most programs relying on sizeof (struct dirent) work well in almost all cases. We really don't want to break that. There appears to be an overlap between these programs and users of
readdir_r, so once we remove that from the API, we should have better story for struct dirent declarators as well.So, it seems like much more could be said about this in documentation. How about the following text for the man page?
DESCRIPTION
[...]
In the glibc implementation, the dirent structure is defined as follows:
struct dirent {
ino_td_ino; /* Inode number */
off_td_off; /* Not an offset; see below */
unsigned shortd_reclen; /* Length of this record */
unsigned chard_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};[...]
NOTES
Thed_namefield
The dirent structure definition shown above is taken from the glibc headers, and shows thed_namefield with a fixed size.Warning: applications should avoid any dependence on the size of the dname field. POSIX defines it as char d_name[], a char‐ acter array of unspecified size, with at most
NAME_MAXcharac‐ ters preceding the terminating null byte ('\0').POSIX.1 explicitly notes that this field should not be used as
an lvalue. The standard also notes that the use of
sizeof(d_name)(and by implication sizeof(struct dirent)) is incorrect; usestrlen(d_name)instead. (On some systems, this field is defined as char d_name[1]!)Note that while the call
fpathconf(fd, _PC_NAME_MAX)returns the value 255 for most filesystems, on some filesystems (e.g., CIFS, Windows SMB servers), the null-terminated filename that is (correctly) returned in
d_namecan actually exceed this size. (In such cases, thed_reclenfield will contain a value that exceeds the size of the glibc dirent structure shown above.)Cheers,
Michael
Signature · 7 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/ -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 1 line · Michael Kerrisk (man-pages)
of the dname field. POSIX defines it as char d_name[], a char‐
Should be “d_name” instead of “dname”.
Otherwise looks fine.
Thanks,
Florian
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 4 lines · Florian Weimer
Quoted 1 line · Michael Kerrisk (man-pages)
of the dname field. POSIX defines it as char d_name[], a char‐Should be “d_name” instead of “dname”.
Fixed now.
Quoted 1 line
Otherwise looks fine.
Thanks, Florian!
Cheers,
Michael
Signature · 7 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/ -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 2 lines · Florian Weimer
Just to be clear, you looked at the wrong struct dirent definition for GNU/Linux, there is a sysdeps override.
Yes, we'd need a similar fix in sysdeps/unix/sysv/linux/bits/dirent.h.
Quoted 5 lines
Right now, most programs relying on sizeof (struct dirent) work well in almost all cases. We really don't want to break that. There appears to be an overlap between these programs and users of
readdir_r, so once we remove that from the API, we should have better story for struct dirent declarators as well.
I see your point in worrying about GNU/Linux programs that use 'sizeof (struct dirent)', even though these programs are not portable and won't work on other POSIX platforms.
How about something like the attached (untested) patch, then? It keeps the structure the same size, while still using flexible arrays to indicate to analyzers that the array in question may be larger. It also adds a compile-time option (default off) to simply use flexible arrays without the backwards-compatibility hack. We could use that option in Gnulib.
bits/dirent.h +2 -3
@@ -32,7 +32,7 @@ struct direntunsigned char d_namlen;/* Length of the file name. */ /* Only this member is in the POSIX standard. */ -char d_name[1 ];/* File name (actually longer). */ +__flexarray (d_name, char,1 );/* File name. */ };#ifdef __USE_LARGEFILE64
@@ -42,8 +42,7 @@ struct dirent64unsigned short int d_reclen;unsigned char d_type;unsigned char d_namlen; - - char d_name[1]; + __flexarray (d_name, char, 1); /* File name. */ };#endif
include/features.h +7 -0
@@ -45,6 +45,7 @@ _THREAD_SAFE Same as _REENTRANT, often used by other systems. _FORTIFY_SOURCE If set to numeric value > 0 additional security measures are defined, according to level. + _FLEXARRAY_SOURCE Select flexible array data structures when applicable. The `-ansi' switch to the GNU C compiler, and standards conformance options such as `-std=c99', define __STRICT_ANSI__. If none of
@@ -80,6 +81,7 @@ __USE_GNU Define GNU extensions. __USE_REENTRANT Define reentrant/thread-safe *_r functions. __USE_FORTIFY_LEVEL Additional security measures used, according to level. + __USE_FLEXARRAY Use flexible arrays for data structures. The macros `__GNU_LIBRARY__', `__GLIBC__', and `__GLIBC_MINOR__' are defined by this file unconditionally. `__GNU_LIBRARY__' is provided
@@ -117,6 +119,7 @@#undef __USE_GNU#undef __USE_REENTRANT#undef __USE_FORTIFY_LEVEL +#undef __USE_FLEXARRAY#undef __KERNEL_STRICT_NAMES/* Suppress kernel-name space pollution unless user expressedly asks
@@ -341,6 +344,10 @@# define __USE_FORTIFY_LEVEL 0#endif +#ifdef _FLEXARRAY_SOURCE +# define __USE_FLEXARRAY 1 +#endif +/* Get definitions of __STDC_* predefined macros, if the compiler has not preincluded this header automatically. */ #include <stdc-predef.h>
misc/sys/cdefs.h +19 -0
@@ -157,10 +157,29 @@# else /* Some other non-C99 compiler. Approximate with [1]. */ # define __flexarr [1] +# define __flexarray (id, type, size) type id[size]# endif # endif #endif +/* Declare a structure member named ID, with type "array of TYPE", and +with actual size unspecified but with nominal size SIZE for the +benefit of traditional applications. If the application is +compiled with _FLEXARRAY_SOURCE and if the compiler is C99 or +better, model this as a flexible array. Otherwise, model it as a +fixed-size array of size SIZE (followed by a flexible array +afterwards, if C99, for the benefit of source-code analyzers that +can use this as a cue that the array is really varying-size). The +fixed-size array is for compatibility with programs that unwisely +take the size of structures that contain flexible arrays, and which +rely on traditional glibc which uses fixed-size arrays. */ +#ifndef __flexarray +# ifdef __USE_FLEXARRAY +# define __flexarray (id, type, size) type id[] +# else +# define __flexarray (id, type, size) type id[size]; type __flexarray __flexarr +# endif +#endif /* __asm__ ("xyz") is used throughout the headers to rename functions at the assembly language level. This is wrapped by the __REDIRECT
sysdeps/nacl/bits/dirent.h +2 -2
@@ -30,7 +30,7 @@ struct direntunsigned short int d_reclen;/* Length of the whole `struct dirent'. */ /* Only this member is in the POSIX standard. */ -char d_name[256 ];/* We must not include limits.h! */ +__flexarray (d_name, char,256 );/* We must not include limits.h! */ };#ifdef __USE_LARGEFILE64
@@ -42,7 +42,7 @@ struct dirent64unsigned short int d_reclen;/* Length of the whole `struct dirent'. */ /* Only this member is in the POSIX standard. */ - char d_name[256]; /* We must not include limits.h! */ + __flexarray (d_name, char, 256); /* We must not include limits.h! */ };#endif
sysdeps/unix/sysv/linux/alpha/bits/dirent.h +2 -2
@@ -29,7 +29,7 @@ struct dirent__off_t d_off;unsigned short int d_reclen;unsigned char d_type; -char d_name[256 ];/* We must not include limits.h! */ +__flexarray (d_name, char,256 );/* We must not include limits.h! */ };#ifdef __USE_LARGEFILE64
@@ -40,7 +40,7 @@ struct dirent64__off64_t d_off;unsigned short int d_reclen;unsigned char d_type; - char d_name[256]; /* We must not include limits.h! */ + __flexarray (d_name, char, 256); /* We must not include limits.h! */ };#endif
sysdeps/unix/sysv/linux/bits/dirent.h +2 -2
@@ -30,7 +30,7 @@ struct dirent#endif unsigned short int d_reclen;unsigned char d_type; -char d_name[256 ];/* We must not include limits.h! */ +__flexarray (d_name, char,256 );/* We must not include limits.h! */ };#ifdef __USE_LARGEFILE64
@@ -40,7 +40,7 @@ struct dirent64__off64_t d_off;unsigned short int d_reclen;unsigned char d_type; - char d_name[256]; /* We must not include limits.h! */ + __flexarray (d_name, char, 256); /* We must not include limits.h! */ };#endif
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 3 lines · Paul Eggert
analyzers that the array in question may be larger. It also adds a compile-time option (default off) to simply use flexible arrays without the backwards-compatibility hack. We could use that option in Gnulib.
I don't think such a compile-time option is desirable (especially not undocumented, all feature test macros should be documented in creature.texi); we shouldn't profilerate feature-test macros for things that aren't well-defined API groups.
Signature · 6 lines
Joseph S. Myers [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 8 lines · Paul Eggert
misc/sys/cdefs.h +8
-0+/* Declare a structure member named ID, with type "array of TYPE", and +with actual size unspecified but with nominal size SIZE for the +benefit of traditional applications. If the application is +compiled with _FLEXARRAY_SOURCE and if the compiler is C99 or +better, model this as a flexible array. Otherwise, model it as a +fixed-size array of size SIZE (followed by a flexible array +afterwards, if C99, for the benefit of source-code analyzers that +can use this as a cue that the array is really varying-size). The
misc/sys/cdefs.h line 182
Quoted 10 lines
misc/sys/cdefs.h +10
-0+fixed-size array is for compatibility with programs that unwisely +take the size of structures that contain flexible arrays, and which +rely on traditional glibc which uses fixed-size arrays. */ +#ifndef __flexarray +# ifdef __USE_FLEXARRAY +# define __flexarray (id, type, size) type id[] +# else +# define __flexarray (id, type, size) type id[size]; type __flexarray __flexarr +# endif +#endif
Sorry, I'm not convinced this is good compiler hint. I suspect it moves things in the opposite direction. Some compilers likely have hacks to support arrays of length 1 as flexible arrays if the array is at the end of a struct, and after your change, the member is no longer at the end, potentially disabling the flexible array kludge.
I think we should interact better with compilers in this area, but this needs fairly explicit mark-up, showing what we need, and not something that requires the compiler to guess.
Thanks,
Florian
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html