diff options
author | Masatake YAMATO <yamato@redhat.com> | 2021-11-22 17:51:54 +0900 |
---|---|---|
committer | Dmitry V. Levin <ldv@strace.io> | 2023-02-20 08:00:00 +0000 |
commit | 2268bd05318f7170ecc2da60271eeb8f63d214dc (patch) | |
tree | 8b2a1fa2c4d8178815cb7f303eabd3e19d9bd43b | |
parent | 765bee80024a6a33ca7724884ab0aaad8e2ddc06 (diff) | |
download | strace-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.h | 5 | ||||
-rw-r--r-- | src/ioctl.c | 6 | ||||
-rw-r--r-- | src/term.c | 39 | ||||
-rw-r--r-- | src/xlat/term_cmds_overlapping.in | 9 | ||||
-rw-r--r-- | tests/ioctl_termios.c | 24 |
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, |