Fix fanotify_mark decoding on 32-bit architectures
The fanotify_mark syscall takes a 64-bit mask, and on 32-bit
architectures it is split up into two syscall arguments.
* configure.ac (AC_CHECK_FUNCS): Add fanotify_mark.
(AC_CHECK_HEADERS): Add sys/fanotify.h.
* defs.h (getllval): New prototype.
* util.c (getllval): New function based on printllval.
(printllval): Use getllval.
* fanotify.c (sys_fanotify_mark): Use getllval to properly decode
64-bit mask and two syscall arguments followed by it.
* tests/fanotify_mark.c: New file.
* tests/fanotify_mark.test: New test.
* tests/Makefile.am (check_PROGRAMS): Add fanotify_mark.
(TESTS): Add fanotify_mark.test.
* tests/.gitignore: Add fanotify_mark.
diff --git a/configure.ac b/configure.ac
index 8cb72d7..1a09b98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -205,6 +205,7 @@
AC_LITTLE_ENDIAN_LONG_LONG
AC_CHECK_FUNCS(m4_normalize([
+ fanotify_mark
fopen64
fork
fputs_unlocked
@@ -239,6 +240,7 @@
stropts.h
sys/conf.h
sys/epoll.h
+ sys/fanotify.h
sys/filio.h
sys/ioctl.h
sys/poll.h
diff --git a/defs.h b/defs.h
index c865110..d0acb19 100644
--- a/defs.h
+++ b/defs.h
@@ -677,6 +677,7 @@
# define LONG_LONG(a,b) \
((long long)((unsigned long long)(unsigned)(b) | ((unsigned long long)(a)<<32)))
#endif
+extern int getllval(struct tcb *, unsigned long long *, int);
extern int printllval(struct tcb *, const char *, int);
extern void printxval(const struct xlat *, const unsigned int, const char *);
diff --git a/fanotify.c b/fanotify.c
index 32a3667..02094ea 100644
--- a/fanotify.c
+++ b/fanotify.c
@@ -31,6 +31,9 @@
int
sys_fanotify_mark(struct tcb *tcp)
{
+ unsigned long long mask = 0;
+ int argn;
+
if (exiting(tcp))
return 0;
@@ -38,13 +41,18 @@
tprints(", ");
printflags(fan_mark_flags, (unsigned) tcp->u_arg[1], "FAN_MARK_???");
tprints(", ");
- printflags(fan_event_flags, tcp->u_arg[2], "FAN_???");
+ /*
+ * the mask argument is defined as 64-bit,
+ * but kernel uses the lower 32 bits only.
+ */
+ argn = getllval(tcp, &mask, 2);
+ printflags(fan_event_flags, mask, "FAN_???");
tprints(", ");
- if ((int) tcp->u_arg[3] == FAN_NOFD)
+ if ((int) tcp->u_arg[argn] == FAN_NOFD)
tprints("FAN_NOFD, ");
else
- print_dirfd(tcp, tcp->u_arg[3]);
- printpath(tcp, tcp->u_arg[4]);
+ print_dirfd(tcp, tcp->u_arg[argn]);
+ printpath(tcp, tcp->u_arg[argn + 1]);
return 0;
}
diff --git a/tests/.gitignore b/tests/.gitignore
index af463d0..ce3e210 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,4 +1,5 @@
caps
+fanotify_mark
inet-accept-connect-send-recv
mmsg
net-accept-connect
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b88ed55..7c8f7b1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,6 +5,7 @@
check_PROGRAMS = \
inet-accept-connect-send-recv \
caps \
+ fanotify_mark \
mmsg \
net-accept-connect \
netlink_inet_diag \
@@ -32,6 +33,7 @@
strace-f.test \
qual_syscall.test \
caps.test \
+ fanotify_mark.test \
getdents.test \
scm_rights-fd.test \
sigaction.test \
diff --git a/tests/fanotify_mark.c b/tests/fanotify_mark.c
new file mode 100644
index 0000000..7160acd
--- /dev/null
+++ b/tests/fanotify_mark.c
@@ -0,0 +1,19 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined HAVE_SYS_FANOTIFY_H && defined HAVE_FANOTIFY_MARK
+# include <sys/fanotify.h>
+int
+main(void)
+{
+ fanotify_mark(-1, FAN_MARK_ADD, FAN_MODIFY | FAN_ONDIR, -100, ".");
+ return 0;
+}
+#else
+int
+main(void)
+{
+ return 77;
+}
+#endif
diff --git a/tests/fanotify_mark.test b/tests/fanotify_mark.test
new file mode 100755
index 0000000..c5eb4eb
--- /dev/null
+++ b/tests/fanotify_mark.test
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# Check fanotify_mark syscall decoding.
+
+. "${srcdir=.}/init.sh"
+
+check_prog grep
+
+./fanotify_mark || {
+ if [ $? -eq 77 ]; then
+ framework_skip_ 'fanotify_mark is not available'
+ else
+ fail_ 'fanotify_mark failed'
+ fi
+}
+
+args="-efanotify_mark ./fanotify_mark"
+$STRACE -o "$LOG" $args || {
+ cat "$LOG"
+ fail_ "$STRACE $args failed"
+}
+
+grep_log()
+{
+ local syscall="$1"; shift
+
+ LC_ALL=C grep -E -x "$syscall$*" $LOG > /dev/null || {
+ cat $LOG
+ fail_ "$STRACE $args failed to trace \"$syscall\" properly"
+ }
+}
+
+grep_log fanotify_mark '\(-1, FAN_MARK_ADD, FAN_MODIFY\|FAN_ONDIR, AT_FDCWD, "\."\) += -1.*'
+
+exit 0
diff --git a/util.c b/util.c
index 570888c..c7ad34f 100644
--- a/util.c
+++ b/util.c
@@ -242,39 +242,39 @@
}
/*
- * Print 64bit argument at position arg_no and return the index of the next
- * argument.
+ * Fetch 64bit argument at position arg_no and
+ * return the index of the next argument.
*/
int
-printllval(struct tcb *tcp, const char *format, int arg_no)
+getllval(struct tcb *tcp, unsigned long long *val, int arg_no)
{
#if SIZEOF_LONG > 4 && SIZEOF_LONG == SIZEOF_LONG_LONG
# if SUPPORTED_PERSONALITIES > 1
if (current_wordsize > 4) {
# endif
- tprintf(format, tcp->u_arg[arg_no]);
+ *val = tcp->u_arg[arg_no];
arg_no++;
# if SUPPORTED_PERSONALITIES > 1
} else {
# if defined(AARCH64) || defined(POWERPC64)
/* Align arg_no to the next even number. */
arg_no = (arg_no + 1) & 0xe;
-# endif
- tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+# endif /* AARCH64 || POWERPC64 */
+ *val = LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]);
arg_no += 2;
}
-# endif /* SUPPORTED_PERSONALITIES */
+# endif /* SUPPORTED_PERSONALITIES > 1 */
#elif SIZEOF_LONG > 4
# error Unsupported configuration: SIZEOF_LONG > 4 && SIZEOF_LONG_LONG > SIZEOF_LONG
#elif defined LINUX_MIPSN32
- tprintf(format, tcp->ext_arg[arg_no]);
+ *val = tcp->ext_arg[arg_no];
arg_no++;
#elif defined X32
if (current_personality == 0) {
- tprintf(format, tcp->ext_arg[arg_no]);
+ *val = tcp->ext_arg[arg_no];
arg_no++;
} else {
- tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+ *val = LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]);
arg_no += 2;
}
#else
@@ -285,7 +285,7 @@
/* Align arg_no to the next even number. */
arg_no = (arg_no + 1) & 0xe;
# endif
- tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+ *val = LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]);
arg_no += 2;
#endif
@@ -293,6 +293,20 @@
}
/*
+ * Print 64bit argument at position arg_no and
+ * return the index of the next argument.
+ */
+int
+printllval(struct tcb *tcp, const char *format, int arg_no)
+{
+ unsigned long long val = 0;
+
+ arg_no = getllval(tcp, &val, arg_no);
+ tprintf(format, val);
+ return arg_no;
+}
+
+/*
* Interpret `xlat' as an array of flags
* print the entries whose bits are on in `flags'
* return # of flags printed.