summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMasatake YAMATO <yamato@redhat.com>2021-11-22 17:51:54 +0900
committerDmitry V. Levin <ldv@strace.io>2023-02-20 08:00:00 +0000
commit2268bd05318f7170ecc2da60271eeb8f63d214dc (patch)
tree8b2a1fa2c4d8178815cb7f303eabd3e19d9bd43b
parent765bee80024a6a33ca7724884ab0aaad8e2ddc06 (diff)
downloadstrace-2268bd05318f7170ecc2da60271eeb8f63d214dc.tar.gz
ioctl: use finfo as hints for resolving overlapping ioctl commands
Such an overlapping can be observed, e.g. when running vi under strace: ioctl(0, SNDCTL_TMR_START or TCSETS, {B38400 ... After this change, when the file descriptor corresponds to a terminal device, strace would be able to resolve the collision between ioctl commands from different subsystems: ioctl(0, TCSETS, {B38400 ... * src/defs.h (term_ioctl_decode_command_number): New function declaration. * src/ioctl.c (ioctl_decode_command_number): Add a new argument for passing finfo that can be used as hints for decoding ioctl commands. Handle the code `T'. (SYS_FUNC(ioctl)): Pass finfo as hints to ioctl_decode_command_number. * src/term.c: Include "xlat/term_cmds_overlapping.h". (term_ioctl_decode_command_number): Decode the command in this earlier stage if the corresponding file descriptor corresponds to a terminal device. * src/xlat/term_cmds_overlapping.in: New file listing overlapping tty ioctl commands. * tests/ioctl_termios.c (main): Extend cmds[] in checks[]. (main::checks::cmds): Add new member "pass_invalid_fd". Omit " or ..." substring in case of TCSETS, TCSETSW, and TCSETSF for a valid tty descriptor. Add test commands for invalid descriptors. Signed-off-by: Masatake YAMATO <yamato@redhat.com> Signed-off-by: Dmitry V. Levin <ldv@strace.io>
-rw-r--r--src/defs.h5
-rw-r--r--src/ioctl.c6
-rw-r--r--src/term.c39
-rw-r--r--src/xlat/term_cmds_overlapping.in9
-rw-r--r--tests/ioctl_termios.c24
5 files changed, 75 insertions, 8 deletions
diff --git a/src/defs.h b/src/defs.h
index 3a2bdcfae..e74948529 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -674,6 +674,11 @@ struct finfo {
extern struct finfo *
get_finfo_for_dev(const char *path, struct finfo *finfo);
+extern int
+term_ioctl_decode_command_number(struct tcb *tcp,
+ const struct finfo *finfo,
+ unsigned int code);
+
/**
* @return 0 on success, -1 on error.
*/
diff --git a/src/ioctl.c b/src/ioctl.c
index 0d3cbe69f..27e4f4906 100644
--- a/src/ioctl.c
+++ b/src/ioctl.c
@@ -242,7 +242,7 @@ hiddev_decode_number(const unsigned int code)
}
static int
-ioctl_decode_command_number(struct tcb *tcp)
+ioctl_decode_command_number(struct tcb *tcp, const struct finfo *finfo)
{
const unsigned int code = tcp->u_arg[1];
@@ -270,6 +270,8 @@ ioctl_decode_command_number(struct tcb *tcp)
return 1;
}
return 0;
+ case 'T':
+ return term_ioctl_decode_command_number(tcp, finfo, code);
case 'U':
if (_IOC_DIR(code) == _IOC_READ && _IOC_NR(code) == 0x2c) {
tprints_arg_begin("UI_GET_SYSNAME");
@@ -472,7 +474,7 @@ SYS_FUNC(ioctl)
if (xlat_verbosity == XLAT_STYLE_VERBOSE)
tprint_comment_begin();
if (xlat_verbosity != XLAT_STYLE_RAW) {
- ret = ioctl_decode_command_number(tcp);
+ ret = ioctl_decode_command_number(tcp, finfo);
if (!(ret & IOCTL_NUMBER_STOP_LOOKUP)) {
iop = ioctl_lookup(tcp->u_arg[1]);
if (iop) {
diff --git a/src/term.c b/src/term.c
index 8b2501307..f392af3fd 100644
--- a/src/term.c
+++ b/src/term.c
@@ -40,6 +40,8 @@
# define termio_cc termios_cc
#endif
+#include "xlat/term_cmds_overlapping.h"
+
static void
decode_oflag(uint64_t val)
{
@@ -449,3 +451,40 @@ term_ioctl(struct tcb *const tcp, const unsigned int code,
return RVAL_IOCTL_DECODED;
}
+
+/*
+ * TTY and SND ioctl commands may clash, for example:
+ *
+ * 0x00005404
+ * { "SNDCTL_TMR_CONTINUE", 0x00005404 },
+ * { "TCSETSF", 0x00005404 },
+ * 0x00005403
+ * { "SNDCTL_TMR_STOP", 0x00005403 },
+ * { "TCSETSW", 0x00005403 },
+ * 0x00005402
+ * { "SNDCTL_TMR_START", 0x00005402 },
+ * { "TCSETS", 0x00005402 },
+ *
+ * This function tries to resolve the collision using the device information
+ * associated with the specified file descriptor.
+ */
+int
+term_ioctl_decode_command_number(struct tcb *tcp,
+ const struct finfo *finfo,
+ unsigned int code)
+{
+ /*
+ * See Linux kernel Documentation/admin-guide/devices.txt
+ */
+ if (finfo
+ && finfo->type == FINFO_DEV_CHR
+ && ((3 <= finfo->dev.major && finfo->dev.major <= 5) ||
+ (136 <= finfo->dev.major && finfo->dev.major <= 143))) {
+ const char *str = xlookup(term_cmds_overlapping, code);
+ if (str) {
+ tprints_string(str);
+ return IOCTL_NUMBER_STOP_LOOKUP;
+ }
+ }
+ return 0;
+}
diff --git a/src/xlat/term_cmds_overlapping.in b/src/xlat/term_cmds_overlapping.in
new file mode 100644
index 000000000..6b0e00338
--- /dev/null
+++ b/src/xlat/term_cmds_overlapping.in
@@ -0,0 +1,9 @@
+/* 0x5401 .. 0x5408 */
+TCGETS
+TCSETS
+TCSETSW
+TCSETSF
+TCGETA
+TCSETA
+TCSETAW
+TCSETAF
diff --git a/tests/ioctl_termios.c b/tests/ioctl_termios.c
index 9b0c94274..eaea4e96b 100644
--- a/tests/ioctl_termios.c
+++ b/tests/ioctl_termios.c
@@ -860,7 +860,8 @@ main(void)
const char *cmd_str;
bool write;
bool can_fail;
- } cmds[6];
+ bool pass_invalid_fd;
+ } cmds[9];
struct {
kernel_ulong_t data;
const char *data_str;
@@ -889,22 +890,33 @@ main(void)
#endif
{
{
- /* XXX */
+ /*
+ * If the fd is valid and points to a tty,
+ * the potential ioctl command collision is resolved.
+ */
+ { ARG_STR(TCSETS), true },
+ { ARG_STR(TCSETSW), true },
+ { ARG_STR(TCSETSF), true },
+
+ /*
+ * If the fd is invalid, it is impossible
+ * to distinguish the overlapping ioctl commands.
+ */
{ TCSETS,
#if IOCTL_CLASHED
"SNDCTL_TMR_START or "
#endif
- "TCSETS", true },
+ "TCSETS", true, true, true },
{ TCSETSW,
#if IOCTL_CLASHED
"SNDCTL_TMR_STOP or "
#endif
- "TCSETSW", true },
+ "TCSETSW", true, true, true },
{ TCSETSF,
#if IOCTL_CLASHED
"SNDCTL_TMR_CONTINUE or "
#endif
- "TCSETSF", true },
+ "TCSETSF", true, true, true },
{ ARG_STR(TCGETS), false },
{ ARG_STR(TIOCSLCKTRMIOS), true, true },
@@ -963,7 +975,7 @@ main(void)
do_ioctl(checks[i].cmds[j].cmd,
checks[i].cmds[j].cmd_str,
- ret,
+ checks[i].cmds[j].pass_invalid_fd? -1: ret,
checks[i].printer,
checks[i].args[k].data,
checks[i].args[k].valid,