compat getdents64: Use correct offset for retry [BZ #23972]

d_off is the offset of the *next* entry, not the offset of the current
entry.
This commit is contained in:
Florian Weimer 2018-12-10 14:14:45 +01:00
parent 80472e2fba
commit 8d20a2f414
3 changed files with 27 additions and 3 deletions

View File

@ -1,3 +1,13 @@
2018-12-10 Florian Weimer <fweimer@redhat.com>
[BZ #23972]
* sysdeps/unix/sysv/linux/getdents64.c (handle_overflow): Check
offset instead of count for clarity. Fix typo in comment.
(__old_getdents64): Keep track of previous offset. Use it to call
handle_overflow.
* sysdeps/unix/sysv/linux/tst-readdir64-compat.c (do_test): Check
that d_off is never zero.
2018-12-10 Andreas Schwab <schwab@suse.de> 2018-12-10 Andreas Schwab <schwab@suse.de>
* sysdeps/unix/sysv/linux/powerpc/powerpc64/*-le.abilist: Move to * sysdeps/unix/sysv/linux/powerpc/powerpc64/*-le.abilist: Move to

View File

@ -41,14 +41,14 @@ handle_overflow (int fd, __off64_t offset, ssize_t count)
{ {
/* If this is the first entry in the buffer, we can report the /* If this is the first entry in the buffer, we can report the
error. */ error. */
if (count == 0) if (offset == 0)
{ {
__set_errno (EOVERFLOW); __set_errno (EOVERFLOW);
return -1; return -1;
} }
/* Otherwise, seek to the overflowing entry, so that the next call /* Otherwise, seek to the overflowing entry, so that the next call
will report the error, and return the data read so far.. */ will report the error, and return the data read so far. */
if (__lseek64 (fd, offset, SEEK_SET) != 0) if (__lseek64 (fd, offset, SEEK_SET) != 0)
return -1; return -1;
return count; return count;
@ -70,6 +70,15 @@ __old_getdents64 (int fd, char *buf, size_t nbytes)
ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes); ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
if (retval > 0) if (retval > 0)
{ {
/* This is the marker for the first entry. Offset 0 is reserved
for the first entry (see rewinddir). Here, we use it as a
marker for the first entry in the buffer. We never actually
seek to offset 0 because handle_overflow reports the error
directly, so it does not matter that the offset is incorrect
if entries have been read from the descriptor before (so that
the descriptor is not actually at offset 0). */
__off64_t previous_offset = 0;
char *p = buf; char *p = buf;
char *end = buf + retval; char *end = buf + retval;
while (p < end) while (p < end)
@ -84,7 +93,7 @@ __old_getdents64 (int fd, char *buf, size_t nbytes)
/* Check for ino_t overflow. */ /* Check for ino_t overflow. */
if (__glibc_unlikely (ino != source->d_ino)) if (__glibc_unlikely (ino != source->d_ino))
return handle_overflow (fd, offset, p - buf); return handle_overflow (fd, previous_offset, p - buf);
/* Convert to the target layout. Use a separate struct and /* Convert to the target layout. Use a separate struct and
memcpy to side-step aliasing issues. */ memcpy to side-step aliasing issues. */
@ -107,6 +116,7 @@ __old_getdents64 (int fd, char *buf, size_t nbytes)
reclen - offsetof (struct dirent64, d_name)); reclen - offsetof (struct dirent64, d_name));
p += reclen; p += reclen;
previous_offset = offset;
} }
} }
return retval; return retval;

View File

@ -88,6 +88,10 @@ do_test (void)
else else
TEST_VERIFY_EXIT (entry_test != NULL); TEST_VERIFY_EXIT (entry_test != NULL);
/* d_off is never zero because it is the offset of the next
entry (not the current entry). */
TEST_VERIFY (entry_reference->d_off > 0);
/* Check that the entries are the same. */ /* Check that the entries are the same. */
TEST_COMPARE_BLOB (entry_reference->d_name, TEST_COMPARE_BLOB (entry_reference->d_name,
strlen (entry_reference->d_name), strlen (entry_reference->d_name),