summaryrefslogtreecommitdiff
path: root/dirent.c
diff options
context:
space:
mode:
authorDmitry V. Levin <ldv@altlinux.org>2020-04-12 00:32:29 +0000
committerDmitry V. Levin <ldv@altlinux.org>2020-04-12 00:32:29 +0000
commit048fd0a024a01770d6b0dde2a1292cf97867a5e1 (patch)
treef8f52fac2ab7d30a70bf4b87169518b91340ee5b /dirent.c
parent9265a9daab24ad950718a5cccd55ff99269d0070 (diff)
downloadstrace-048fd0a024a01770d6b0dde2a1292cf97867a5e1.tar.gz
Rewrite decoders of getdents, getdents64, and readdir syscalls
The old approach of allocating memory for all dentries returned by getdents/getdents64 was problematic, fix it by fetching and printing dentries sequentially. * dirent_types.c: New file. * xgetdents.c: Likewise. * xgetdents.h: Likewise. * Makefile.am (strace_SOURCES): Add them. * dirent.c: Include "xgetdents.h" and "print_fields.h". (header_size): New variable. (print_dentry_head, decode_dentry_head, decode_dentry_tail): New functions. (print_old_dirent): Rewrite using print_dentry_head. (SYS_FUNC(getdents)): Rewrite using xgetdents, decode_dentry_head, and decode_dentry_tail. * dirent64.c: Include "xgetdents.h", "kernel_dirent.h" and "print_fields.h" instead of "defs.h", <dirent.h> and "xlat/dirent_types.h". (decode_dentry_head, decode_dentry_tail): New functions. (SYS_FUNC(getdents64)): Rewrite using xgetdents, decode_dentry_head, and decode_dentry_tail. Resolves: https://github.com/strace/strace/issues/19 Resolves: https://github.com/strace/strace/pull/20
Diffstat (limited to 'dirent.c')
-rw-r--r--dirent.c169
1 files changed, 70 insertions, 99 deletions
diff --git a/dirent.c b/dirent.c
index 98ee85292..6e4d09a54 100644
--- a/dirent.c
+++ b/dirent.c
@@ -17,22 +17,82 @@
#include MPERS_DEFS
+#include "xgetdents.h"
+#include "print_fields.h"
+
#define D_NAME_LEN_MAX 256
+/* The minimum size of a valid directory entry. */
+static const unsigned int header_size =
+ offsetof(kernel_dirent_t, d_name);
+
+static void
+print_dentry_head(const kernel_dirent_t *const dent)
+{
+ PRINT_FIELD_U("{", *dent, d_ino);
+ PRINT_FIELD_U(", ", *dent, d_off);
+ PRINT_FIELD_U(", ", *dent, d_reclen);
+}
+
+static unsigned int
+decode_dentry_head(struct tcb *const tcp, const void *const arg)
+{
+ const kernel_dirent_t *const dent = arg;
+
+ if (!abbrev(tcp))
+ print_dentry_head(dent);
+
+ return dent->d_reclen;
+}
+
+static int
+decode_dentry_tail(struct tcb *const tcp, kernel_ulong_t addr,
+ const void *const arg, const unsigned int d_name_type_len)
+{
+ int rc = 0;
+
+ /* !abbrev(tcp) */
+
+ if (d_name_type_len) {
+ unsigned int d_name_len = d_name_type_len - 1;
+ if (d_name_len) {
+ if (d_name_len > D_NAME_LEN_MAX)
+ d_name_len = D_NAME_LEN_MAX;
+ tprints(", d_name=");
+ rc = printpathn(tcp, addr, d_name_len - 1);
+ }
+ tprints(", d_type=");
+ const kernel_ulong_t d_type_addr =
+ addr + (d_name_type_len - 1);
+ unsigned char d_type;
+ if (umove_or_printaddr(tcp, d_type_addr, &d_type))
+ rc = -1;
+ else
+ printxval(dirent_types, d_type, "DT_???");
+ }
+ tprints("}");
+
+ return rc;
+}
+
+SYS_FUNC(getdents)
+{
+ return xgetdents(tcp, header_size,
+ decode_dentry_head, decode_dentry_tail);
+}
+
static void
print_old_dirent(struct tcb *const tcp, const kernel_ulong_t addr)
{
- kernel_dirent_t d;
+ kernel_dirent_t dent;
- if (umove_or_printaddr(tcp, addr, &d))
+ if (umove_or_printaddr(tcp, addr, &dent))
return;
- tprintf("{d_ino=%llu, d_off=%llu, d_reclen=%u, d_name=",
- zero_extend_signed_to_ull(d.d_ino),
- zero_extend_signed_to_ull(d.d_off), d.d_reclen);
- if (d.d_reclen > D_NAME_LEN_MAX)
- d.d_reclen = D_NAME_LEN_MAX;
- printpathn(tcp, addr + offsetof(kernel_dirent_t, d_name), d.d_reclen);
+ print_dentry_head(&dent);
+ tprints(", d_name=");
+ printpathn(tcp, addr + header_size,
+ MIN(dent.d_reclen, D_NAME_LEN_MAX));
tprints("}");
}
@@ -46,99 +106,10 @@ SYS_FUNC(readdir)
printaddr(tcp->u_arg[1]);
else
print_old_dirent(tcp, tcp->u_arg[1]);
+ const unsigned int count = tcp->u_arg[2];
/* Not much point in printing this out, it is always 1. */
- if (tcp->u_arg[2] != 1)
- tprintf(", %" PRI_klu, tcp->u_arg[2]);
- }
- return 0;
-}
-
-SYS_FUNC(getdents)
-{
- unsigned int i, len, dents = 0;
- unsigned char *buf;
-
- if (entering(tcp)) {
- printfd(tcp, tcp->u_arg[0]);
- return 0;
- }
-
- tprints(", ");
-
- const unsigned int count = tcp->u_arg[2];
-
- if (syserror(tcp) || !verbose(tcp) ||
- (kernel_ulong_t) tcp->u_rval > count /* kernel gone bananas? */) {
- printaddr(tcp->u_arg[1]);
- tprintf(", %u", count);
- return 0;
- }
-
- /* Beware of insanely large or too small values in tcp->u_rval */
- if (tcp->u_rval > 1024*1024)
- len = 1024*1024;
- else if (tcp->u_rval < (int) sizeof(kernel_dirent_t))
- len = 0;
- else
- len = tcp->u_rval;
-
- if (len) {
- buf = malloc(len);
- if (!buf || umoven(tcp, tcp->u_arg[1], len, buf) < 0) {
- printaddr(tcp->u_arg[1]);
+ if (count != 1)
tprintf(", %u", count);
- free(buf);
- return 0;
- }
- } else {
- buf = NULL;
- }
-
- if (abbrev(tcp))
- printaddr(tcp->u_arg[1]);
- else
- tprints("[");
-
- for (i = 0; len && i <= len - sizeof(kernel_dirent_t); ) {
- kernel_dirent_t *d = (kernel_dirent_t *) &buf[i];
-
- if (!abbrev(tcp)) {
- int oob = d->d_reclen < sizeof(kernel_dirent_t) ||
- i + d->d_reclen - 1 >= len;
- int d_name_len = oob ? len - i : d->d_reclen;
- d_name_len -= offsetof(kernel_dirent_t, d_name) + 1;
- if (d_name_len > D_NAME_LEN_MAX)
- d_name_len = D_NAME_LEN_MAX;
-
- tprintf("%s{d_ino=%llu, d_off=%llu, d_reclen=%u"
- ", d_name=", i ? ", " : "",
- zero_extend_signed_to_ull(d->d_ino),
- zero_extend_signed_to_ull(d->d_off),
- d->d_reclen);
-
- print_quoted_cstring(d->d_name, d_name_len);
-
- tprints(", d_type=");
- if (oob)
- tprints("?");
- else
- printxval(dirent_types, buf[i + d->d_reclen - 1], "DT_???");
- tprints("}");
- }
- dents++;
- if (d->d_reclen < sizeof(kernel_dirent_t)) {
- tprints_comment("d_reclen < sizeof(struct dirent)");
- break;
- }
- i += d->d_reclen;
}
-
- if (abbrev(tcp))
- tprintf_comment("%u entries", dents);
- else
- tprints("]");
-
- tprintf(", %u", count);
- free(buf);
return 0;
}