diff options
| author | Adil Alperen Çiftci <[email protected]> | 2026-11-05 11:11:50 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-11-05 16:11:50 +0800 |
| commit | 40e07d970ecac0a12122c01e15f39dcb2441479b (patch) | |
| tree | 2cf4858dd953a85ac880cecceac00322f3308d8c | |
| parent | 7feb0edadb711f731ed12dc30d5b1c51f85b6a89 (diff) | |
[smart] lwp: enforce user buffer validation and copyin/copyout in terminal ioctl (#11941)
* [smart] lwp: enforce user buffer validation and copyin/copyout in terminal ioctl
In the RT-Smart terminal subsystem, userspace ioctl commands (such as TIOCGWINSZ, TIOCSWINSZ, TIOCGPGRP, TIOCGSID, FIONREAD, and TIOCGPTN) were previously passed directly to underlying FreeBSD ioctl handlers as raw user pointers without kernel staging or copy validation.
This patch addresses the user pointer dereference sinks:
1. In lwp_tty_ioctl_adapter(), stage arguments in kernel stack buffers and use _copy_from_user() / _copy_to_user() (lwp_get_from_user / lwp_put_to_user) for TIOCGWINSZ, TIOCSWINSZ, TIOCGPGRP, TIOCSPGRP, TIOCGSID, TIOCSTI, TIOCOUTQ, FIONREAD, and modem commands.
2. In ptm_fops_ioctl(), safely buffer arguments for TIOCGPTN, TIOCPKT, TIOCSIG, TIOCGPGRP, TIOCGSID, FIONREAD, TIOCOUTQ, TIOCGWINSZ, TIOCSWINSZ, and termios settings, returning -ENOTTY on unrecognized commands.
3. In tty_generic_ioctl(), add defense-in-depth boundary checks using lwp_in_user_space() to safeguard kernel memory from arbitrary pointer dereferences.
Signed-off-by: Adil Alperen Çiftci <[email protected]>
* [smart] lwp: make the tty ioctl adapters the single uaccess boundary
lwp_tty_ioctl_adapter() and ptm_fops_ioctl() (through
lwp_ptmx_ioctl_forward()) already stage every user argument into a kernel
buffer before entering the BSD tty layer, so tty_generic_ioctl() only ever
sees kernel pointers on the syscall path. Drop the lwp_in_user_space()
branches that were added there, and the older one on TIOCSPGRP, so the
trust boundary is in exactly one place per entry point and the switch no
longer mixes two models.
Zero the staging union in lwp_ptmx_ioctl_forward() before the backend
runs: an *_OUT command copies it back to the caller, and a backend that
returns 0 without filling it would otherwise hand out stack contents.
Add the BSD termios commands TIOCGETA, TIOCSETA, TIOCSETAW and TIOCSETAF
to the /dev/ptmx argument table so they keep reaching the tty layer as
they did before the table existed. The termio-based TCGETA family is not
handled by the BSD layer on this path and stays ENOTTY, as before.
Rename lwp_ptmx_ioctl_arg_type() to lwp_ptmx_ioctl_arg_type_get() so the
function no longer shares its name with the enum tag, rename the test
case to testcases.lwp.tty_ptmx.regression since it covers more than the
buffer bounds, and add the terminal directory to the utest CPPPATH
instead of a relative include.
Signed-off-by: Adil Alperen Ciftci <[email protected]>
* [smart] lwp: drop duplicate termios cases from the ptmx ioctl table
In this port TIOCGETA and TIOCSETA/TIOCSETAW/TIOCSETAF are defined to the
same values as TCGETS and TCSETS/TCSETSW/TCSETSF, so listing both sets
makes the switch fail to compile with duplicate case values. The TCGETS
family entries already cover the BSD names; the test asserts for the BSD
spellings stay valid through the aliases.
Signed-off-by: Adil Alperen Ciftci <[email protected]>
---------
Signed-off-by: Adil Alperen Çiftci <[email protected]>
Signed-off-by: Adil Alperen Ciftci <[email protected]>
Co-authored-by: Adil Alperen Çiftci <[email protected]>
Co-authored-by: Rbb666 <[email protected]>
| -rw-r--r-- | components/lwp/terminal/freebsd/tty.c | 12 | ||||
| -rw-r--r-- | components/lwp/terminal/freebsd/tty_compat.c | 115 | ||||
| -rw-r--r-- | components/lwp/terminal/tty_internal.h | 25 | ||||
| -rw-r--r-- | components/lwp/terminal/tty_ptmx.c | 244 | ||||
| -rw-r--r-- | components/lwp/utest/Kconfig | 6 | ||||
| -rw-r--r-- | components/lwp/utest/SConscript | 3 | ||||
| -rw-r--r-- | components/lwp/utest/tty_ptmx_tc.c | 155 |
7 files changed, 503 insertions, 57 deletions
diff --git a/components/lwp/terminal/freebsd/tty.c b/components/lwp/terminal/freebsd/tty.c index e5a6e79f13..622c4dd975 100644 --- a/components/lwp/terminal/freebsd/tty.c +++ b/components/lwp/terminal/freebsd/tty.c @@ -1289,16 +1289,8 @@ static int tty_generic_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data, case TIOCSCTTY: return lwp_tty_set_ctrl_proc(tp, td); case TIOCSPGRP: { - int pgid; - if (lwp_in_user_space((void *)data)) - { - if (lwp_get_from_user(&pgid, data, sizeof(int)) != sizeof(int)) - return -EFAULT; - } - else - { - pgid = *(int *)data; - } + /* data is a kernel buffer: the ioctl adapters are the uaccess boundary */ + int pgid = *(int *)data; return lwp_tty_assign_foreground(tp, td, pgid); } case TIOCFLUSH: { diff --git a/components/lwp/terminal/freebsd/tty_compat.c b/components/lwp/terminal/freebsd/tty_compat.c index 338489d549..10e97ed9f6 100644 --- a/components/lwp/terminal/freebsd/tty_compat.c +++ b/components/lwp/terminal/freebsd/tty_compat.c @@ -507,45 +507,122 @@ int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thre /* Controlling terminal */ case TIOCSCTTY: case TIOCNOTTY: + error = IOCTL(cmd, RT_NULL, fflags, td); + break; /* Process group and session ID */ case TIOCGPGRP: + { + int pgrp; + error = IOCTL(TIOCGPGRP, (rt_caddr_t)&pgrp, fflags, td); + if (!error) + { + error = _copy_to_user(args, &pgrp, sizeof(pgrp)); + } + break; + } + case TIOCSPGRP: + { + int pgrp; + error = _copy_from_user(&pgrp, args, sizeof(pgrp)); + if (!error) + { + error = IOCTL(TIOCSPGRP, (rt_caddr_t)&pgrp, fflags, td); + } + break; + } + case TIOCGSID: + { + int sid; + error = IOCTL(TIOCGSID, (rt_caddr_t)&sid, fflags, td); + if (!error) + { + error = _copy_to_user(args, &sid, sizeof(sid)); + } + break; + } - /* TIOCOUTQ */ - /* TIOCSTI */ case TIOCGWINSZ: + { + struct winsize ws; + error = IOCTL(TIOCGWINSZ, (rt_caddr_t)&ws, fflags, td); + if (!error) + { + error = _copy_to_user(args, &ws, sizeof(ws)); + } + break; + } + case TIOCSWINSZ: - error = IOCTL(cmd, (rt_caddr_t)args, fflags, td); + { + struct winsize ws; + error = _copy_from_user(&ws, args, sizeof(ws)); + if (!error) + { + error = IOCTL(TIOCSWINSZ, (rt_caddr_t)&ws, fflags, td); + } break; -#ifdef USING_BSD_IOCTL_EXT - case TIOCMGET: - args->cmd = TIOCMGET; - error = (sys_ioctl(td, (struct ioctl_args *)args)); + } + + case TIOCSTI: + { + char ch; + error = _copy_from_user(&ch, args, sizeof(ch)); + if (!error) + { + error = IOCTL(TIOCSTI, (rt_caddr_t)&ch, fflags, td); + } break; + } - case TIOCMBIS: - args->cmd = TIOCMBIS; - error = (sys_ioctl(td, (struct ioctl_args *)args)); + case TIOCOUTQ: + case FIONWRITE: + { + int nout; + error = IOCTL(cmd, (rt_caddr_t)&nout, fflags, td); + if (!error) + { + error = _copy_to_user(args, &nout, sizeof(nout)); + } break; + } - case TIOCMBIC: - args->cmd = TIOCMBIC; - error = (sys_ioctl(td, (struct ioctl_args *)args)); + case TIOCMGET: + { + int bits; + error = IOCTL(TIOCMGET, (rt_caddr_t)&bits, fflags, td); + if (!error) + { + error = _copy_to_user(args, &bits, sizeof(bits)); + } break; + } + case TIOCMBIS: + case TIOCMBIC: case TIOCMSET: - args->cmd = TIOCMSET; - error = (sys_ioctl(td, (struct ioctl_args *)args)); + { + int bits; + error = _copy_from_user(&bits, args, sizeof(bits)); + if (!error) + { + error = IOCTL(cmd, (rt_caddr_t)&bits, fflags, td); + } break; -#endif /* USING_BSD_IOCTL_EXT */ - /* TIOCGSOFTCAR */ - /* TIOCSSOFTCAR */ + } case FIONREAD: /* TIOCINQ */ - error = (IOCTL(FIONREAD, args, fflags, td)); + { + int nread; + error = IOCTL(FIONREAD, (rt_caddr_t)&nread, fflags, td); + if (!error) + { + error = _copy_to_user(args, &nread, sizeof(nread)); + } break; + } case TIOCLINUX: { diff --git a/components/lwp/terminal/tty_internal.h b/components/lwp/terminal/tty_internal.h index 8bf336cb89..fdd563e09a 100644 --- a/components/lwp/terminal/tty_internal.h +++ b/components/lwp/terminal/tty_internal.h @@ -17,6 +17,31 @@ extern struct cdevsw bsd_ttydev_methods; extern struct bsd_fileops bsd_ptsdev_methods; +enum lwp_ptmx_ioctl_arg_type +{ + LWP_PTMX_IOCTL_UNSUPPORTED, + LWP_PTMX_IOCTL_NONE, + LWP_PTMX_IOCTL_INT_IN, + LWP_PTMX_IOCTL_INT_OUT, + LWP_PTMX_IOCTL_UINT_OUT, + LWP_PTMX_IOCTL_CHAR_IN, + LWP_PTMX_IOCTL_WINSIZE_IN, + LWP_PTMX_IOCTL_WINSIZE_OUT, + LWP_PTMX_IOCTL_TERMIOS_IN, + LWP_PTMX_IOCTL_TERMIOS_OUT, +}; + +enum lwp_ptmx_ioctl_arg_type lwp_ptmx_ioctl_arg_type_get(rt_ubase_t cmd); + +typedef rt_size_t (*lwp_ptmx_copy_fn)(void *dst, void *src, rt_size_t size, + void *context); +typedef int (*lwp_ptmx_ioctl_fn)(rt_ubase_t cmd, void *arg, void *context); + +int lwp_ptmx_ioctl_forward(rt_ubase_t cmd, void *arg, + lwp_ptmx_copy_fn copy_from_user, + lwp_ptmx_copy_fn copy_to_user, + lwp_ptmx_ioctl_fn ioctl, void *context); + /* bsd devsw porting */ void bsd_devsw_init(struct lwp_ttydevsw *tsw); diff --git a/components/lwp/terminal/tty_ptmx.c b/components/lwp/terminal/tty_ptmx.c index 5938bf0103..027c146e9d 100644 --- a/components/lwp/terminal/tty_ptmx.c +++ b/components/lwp/terminal/tty_ptmx.c @@ -38,7 +38,9 @@ static int ptm_fops_open(struct dfs_file *file) /* detached operation from devfs */ if (rc == 0) + { file->vnode->fops = &ptm_fops; + } } else { @@ -157,6 +159,199 @@ static ssize_t ptm_fops_write(struct dfs_file *file, const void *buf, return rc; } +enum lwp_ptmx_ioctl_arg_type lwp_ptmx_ioctl_arg_type_get(rt_ubase_t cmd) +{ + switch (cmd) + { + case TIOCSDTR: + case TIOCCDTR: + case TIOCNOTTY: + case TIOCSCTTY: + case TIOCDRAIN: + case TIOCEXCL: + case TIOCNXCL: + case TIOCSTOP: + case TIOCSTART: + case TIOCSTAT: + case TIOCSBRK: + case TIOCCBRK: + return LWP_PTMX_IOCTL_NONE; + + case TIOCPKT: + case TIOCSIG: + case FIONBIO: + case TIOCSPGRP: + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + case FIOASYNC: + case FIOSETOWN: + case TIOCFLUSH: + case TIOCSDRAINWAIT: + case TIOCCONS: + case TIOCSETD: + return LWP_PTMX_IOCTL_INT_IN; + + case TIOCGPGRP: + case TIOCGSID: + case FIONREAD: + case TIOCOUTQ: + case FIONWRITE: + case TIOCMGET: + case FIOGETOWN: + case TIOCGETD: + case TIOCGDRAINWAIT: + return LWP_PTMX_IOCTL_INT_OUT; + + case TIOCGPTN: + return LWP_PTMX_IOCTL_UINT_OUT; + + case TIOCSTI: + return LWP_PTMX_IOCTL_CHAR_IN; + + case TIOCSWINSZ: + return LWP_PTMX_IOCTL_WINSIZE_IN; + + case TIOCGWINSZ: + return LWP_PTMX_IOCTL_WINSIZE_OUT; + + case TCSETS: + case TCSETSW: + case TCSETSF: + return LWP_PTMX_IOCTL_TERMIOS_IN; + + case TCGETS: + return LWP_PTMX_IOCTL_TERMIOS_OUT; + + default: + return LWP_PTMX_IOCTL_UNSUPPORTED; + } +} + +int lwp_ptmx_ioctl_forward(rt_ubase_t cmd, void *arg, + lwp_ptmx_copy_fn copy_from_user, + lwp_ptmx_copy_fn copy_to_user, + lwp_ptmx_ioctl_fn ioctl, void *context) +{ + int rc; + rt_size_t arg_size = 0; + rt_bool_t copy_out = RT_FALSE; + void *kernel_arg = RT_NULL; + enum lwp_ptmx_ioctl_arg_type arg_type; + union + { + int int_value; + unsigned int uint_value; + char char_value; + struct winsize winsize_value; + struct termios termios_value; + } value; + + /* *_OUT commands copy this back to the caller; never expose stale stack */ + rt_memset(&value, 0, sizeof(value)); + + arg_type = lwp_ptmx_ioctl_arg_type_get(cmd); + switch (arg_type) + { + case LWP_PTMX_IOCTL_NONE: + break; + case LWP_PTMX_IOCTL_INT_IN: + kernel_arg = &value.int_value; + arg_size = sizeof(value.int_value); + break; + case LWP_PTMX_IOCTL_INT_OUT: + kernel_arg = &value.int_value; + arg_size = sizeof(value.int_value); + copy_out = RT_TRUE; + break; + case LWP_PTMX_IOCTL_UINT_OUT: + kernel_arg = &value.uint_value; + arg_size = sizeof(value.uint_value); + copy_out = RT_TRUE; + break; + case LWP_PTMX_IOCTL_CHAR_IN: + kernel_arg = &value.char_value; + arg_size = sizeof(value.char_value); + break; + case LWP_PTMX_IOCTL_WINSIZE_IN: + kernel_arg = &value.winsize_value; + arg_size = sizeof(value.winsize_value); + break; + case LWP_PTMX_IOCTL_WINSIZE_OUT: + kernel_arg = &value.winsize_value; + arg_size = sizeof(value.winsize_value); + copy_out = RT_TRUE; + break; + case LWP_PTMX_IOCTL_TERMIOS_IN: + kernel_arg = &value.termios_value; + arg_size = sizeof(value.termios_value); + break; + case LWP_PTMX_IOCTL_TERMIOS_OUT: + kernel_arg = &value.termios_value; + arg_size = sizeof(value.termios_value); + copy_out = RT_TRUE; + break; + default: + return -ENOTTY; + } + + if (arg_size && !copy_out && + copy_from_user(kernel_arg, arg, arg_size, context) != arg_size) + { + return -EFAULT; + } + + rc = ioctl(cmd, kernel_arg, context); + if (!rc && copy_out && + copy_to_user(arg, kernel_arg, arg_size, context) != arg_size) + { + rc = -EFAULT; + } + + return rc; +} + +struct ptm_ioctl_context +{ + struct dfs_file *file; + lwp_tty_t tp; +}; + +static rt_size_t ptm_copy_from_user(void *dst, void *src, rt_size_t size, + void *context) +{ + RT_UNUSED(context); + return lwp_get_from_user(dst, src, size); +} + +static rt_size_t ptm_copy_to_user(void *dst, void *src, rt_size_t size, + void *context) +{ + RT_UNUSED(context); + return lwp_put_to_user(dst, src, size); +} + +static int ptm_ioctl_backend(rt_ubase_t cmd, void *arg, void *context) +{ + struct ptm_ioctl_context *ptm_context = context; + + return bsd_ptsdev_methods.fo_ioctl( + ptm_context->tp, cmd, arg, 0, FFLAGS(ptm_context->file->flags), + rt_thread_self()); +} + +static int ptm_ioctl_forward(struct dfs_file *file, lwp_tty_t tp, + rt_ubase_t cmd, void *arg) +{ + struct ptm_ioctl_context context = { + .file = file, + .tp = tp, + }; + + return lwp_ptmx_ioctl_forward(cmd, arg, ptm_copy_from_user, + ptm_copy_to_user, ptm_ioctl_backend, &context); +} + static int ptm_fops_ioctl(struct dfs_file *file, int cmd, void *arg) { int rc; @@ -171,35 +366,40 @@ static int ptm_fops_ioctl(struct dfs_file *file, int cmd, void *arg) switch (cmd_normal) { - case TIOCSPTLCK: + case TIOCSPTLCK: + { + int is_lock; + if (lwp_get_from_user(&is_lock, arg, sizeof(int)) != sizeof(int)) { - int is_lock; - if (lwp_get_from_user(&is_lock, arg, sizeof(int)) != sizeof(int)) - return -EFAULT; - pts_set_lock(tp, is_lock); - rc = 0; + return -EFAULT; } - break; - case TIOCGPTLCK: + pts_set_lock(tp, is_lock); + rc = 0; + } + break; + case TIOCGPTLCK: + { + int is_lock = pts_is_locked(tp); + if (lwp_put_to_user(arg, &is_lock, sizeof(int)) != sizeof(int)) { - int is_lock = pts_is_locked(tp); - if (lwp_put_to_user(arg, &is_lock, sizeof(int)) != sizeof(int)) - return -EFAULT; - rc = 0; + return -EFAULT; } - break; - case TIOCGPKT: + rc = 0; + } + break; + case TIOCGPKT: + { + int pktmode = pts_get_pktmode(tp); + if (lwp_put_to_user(arg, &pktmode, sizeof(int)) != sizeof(int)) { - int pktmode = pts_get_pktmode(tp); - if (lwp_put_to_user(arg, &pktmode, sizeof(int)) != sizeof(int)) - return -EFAULT; - rc = 0; + return -EFAULT; } + rc = 0; + } + break; + default: + rc = ptm_ioctl_forward(file, tp, cmd_normal, arg); break; - default: - rc = bsd_ptsdev_methods.fo_ioctl( - tp, cmd_normal, arg, 0, FFLAGS(file->flags), rt_thread_self()); - break; } } else diff --git a/components/lwp/utest/Kconfig b/components/lwp/utest/Kconfig index 2901f8f86a..7bdcfefbea 100644 --- a/components/lwp/utest/Kconfig +++ b/components/lwp/utest/Kconfig @@ -6,11 +6,11 @@ config RT_UTEST_LWP default n config RT_UTEST_LWP_TTY_PTMX - bool "Enable Utest for tty_ptmx buffer overflow regression (V-004)" + bool "Enable tty_ptmx regression tests" depends on RT_USING_SMART default n help - Regression guard for the snprintf buffer bounds fix in - components/lwp/terminal/tty_ptmx.c. + Regression guards for tty_ptmx buffer bounds and ioctl argument + handling. endmenu diff --git a/components/lwp/utest/SConscript b/components/lwp/utest/SConscript index b944afe189..a68856f292 100644 --- a/components/lwp/utest/SConscript +++ b/components/lwp/utest/SConscript @@ -1,9 +1,10 @@ +import os Import('rtconfig') from building import * cwd = GetCurrentDir() src = [] -CPPPATH = [cwd] +CPPPATH = [cwd, os.path.join(cwd, '..', 'terminal')] if GetDepend(['RT_UTEST_LWP', 'RT_USING_SMART']): src += ['condvar_timedwait_tc.c', 'condvar_broadcast_tc.c', 'condvar_signal_tc.c'] diff --git a/components/lwp/utest/tty_ptmx_tc.c b/components/lwp/utest/tty_ptmx_tc.c index 74416bf5ef..592d209386 100644 --- a/components/lwp/utest/tty_ptmx_tc.c +++ b/components/lwp/utest/tty_ptmx_tc.c @@ -32,12 +32,13 @@ * - Software configuration: RT_USING_SMART must be enabled. * * Expected Results: - * - Final output: "[ PASSED ] [ result ] testcase (testcases.lwp.tty_ptmx.buffer_bounds)" + * - Final output: "[ PASSED ] [ result ] testcase (testcases.lwp.tty_ptmx.regression)" * - No assertion failures during test execution. */ #include "utest_assert.h" +#include "tty_internal.h" #include <rtdef.h> #include <string.h> #include <stdio.h> @@ -45,6 +46,71 @@ #define DEV_REL_PATH "/ptmx" #define CANARY 0xAB +struct tty_ptmx_forward_state +{ + int copy_from_calls; + int copy_to_calls; + int ioctl_calls; + int fail_copy_from; + int fail_copy_to; + int output_value; + int forwarded_value; + rt_size_t copy_size; + rt_ubase_t forwarded_cmd; + void *forwarded_arg; +}; + +static rt_size_t tty_ptmx_copy_from(void *dst, void *src, rt_size_t size, + void *context) +{ + struct tty_ptmx_forward_state *state = context; + + state->copy_from_calls++; + state->copy_size = size; + if (state->fail_copy_from) + { + return 0; + } + memcpy(dst, src, size); + return size; +} + +static rt_size_t tty_ptmx_copy_to(void *dst, void *src, rt_size_t size, + void *context) +{ + struct tty_ptmx_forward_state *state = context; + + state->copy_to_calls++; + state->copy_size = size; + if (state->fail_copy_to) + { + return 0; + } + memcpy(dst, src, size); + return size; +} + +static int tty_ptmx_forward_ioctl(rt_ubase_t cmd, void *arg, void *context) +{ + struct tty_ptmx_forward_state *state = context; + + state->ioctl_calls++; + state->forwarded_cmd = cmd; + state->forwarded_arg = arg; + if (arg) + { + if (cmd == TIOCMGET) + { + *(int *)arg = state->output_value; + } + else + { + state->forwarded_value = *(int *)arg; + } + } + return 0; +} + static void tty_ptmx_buffer_bounds_tc(void) { /* Simulate the allocation and snprintf call from lwp_ptmx_init(). */ @@ -89,6 +155,89 @@ static void tty_ptmx_buffer_bounds_tc(void) } } +static void tty_ptmx_ioctl_arg_type_tc(void) +{ + uassert_int_equal(lwp_ptmx_ioctl_arg_type_get(TIOCSPGRP), LWP_PTMX_IOCTL_INT_IN); + uassert_int_equal(lwp_ptmx_ioctl_arg_type_get(TIOCMGET), LWP_PTMX_IOCTL_INT_OUT); + uassert_int_equal(lwp_ptmx_ioctl_arg_type_get(TIOCSTI), LWP_PTMX_IOCTL_CHAR_IN); + uassert_int_equal(lwp_ptmx_ioctl_arg_type_get(TIOCEXCL), LWP_PTMX_IOCTL_NONE); + uassert_int_equal(lwp_ptmx_ioctl_arg_type_get(TIOCGETA), LWP_PTMX_IOCTL_TERMIOS_OUT); + uassert_int_equal(lwp_ptmx_ioctl_arg_type_get(TIOCSETAW), LWP_PTMX_IOCTL_TERMIOS_IN); + uassert_int_equal(lwp_ptmx_ioctl_arg_type_get(0xffffffffU), LWP_PTMX_IOCTL_UNSUPPORTED); +} + +static void tty_ptmx_ioctl_forward_tc(void) +{ + struct tty_ptmx_forward_state state; + int value; + int rc; + + memset(&state, 0, sizeof(state)); + value = 73; + rc = lwp_ptmx_ioctl_forward(TIOCSPGRP, &value, tty_ptmx_copy_from, + tty_ptmx_copy_to, tty_ptmx_forward_ioctl, &state); + uassert_int_equal(rc, 0); + uassert_int_equal(state.copy_from_calls, 1); + uassert_int_equal(state.copy_to_calls, 0); + uassert_int_equal(state.ioctl_calls, 1); + uassert_int_equal(state.copy_size, sizeof(value)); + uassert_int_equal(state.forwarded_cmd, TIOCSPGRP); + uassert_int_equal(state.forwarded_value, value); + + memset(&state, 0, sizeof(state)); + state.output_value = 41; + value = 0; + rc = lwp_ptmx_ioctl_forward(TIOCMGET, &value, tty_ptmx_copy_from, + tty_ptmx_copy_to, tty_ptmx_forward_ioctl, &state); + uassert_int_equal(rc, 0); + uassert_int_equal(state.copy_from_calls, 0); + uassert_int_equal(state.copy_to_calls, 1); + uassert_int_equal(state.ioctl_calls, 1); + uassert_int_equal(value, state.output_value); + + memset(&state, 0, sizeof(state)); + value = 1; + rc = lwp_ptmx_ioctl_forward(FIONBIO, &value, tty_ptmx_copy_from, + tty_ptmx_copy_to, tty_ptmx_forward_ioctl, &state); + uassert_int_equal(rc, 0); + uassert_int_equal(state.copy_from_calls, 1); + uassert_int_equal(state.forwarded_value, value); + + memset(&state, 0, sizeof(state)); + rc = lwp_ptmx_ioctl_forward(TIOCEXCL, RT_NULL, tty_ptmx_copy_from, + tty_ptmx_copy_to, tty_ptmx_forward_ioctl, &state); + uassert_int_equal(rc, 0); + uassert_int_equal(state.copy_from_calls, 0); + uassert_int_equal(state.copy_to_calls, 0); + uassert_int_equal(state.ioctl_calls, 1); + uassert_null(state.forwarded_arg); + + memset(&state, 0, sizeof(state)); + state.fail_copy_from = 1; + value = 73; + rc = lwp_ptmx_ioctl_forward(TIOCSPGRP, &value, tty_ptmx_copy_from, + tty_ptmx_copy_to, tty_ptmx_forward_ioctl, &state); + uassert_int_equal(rc, -EFAULT); + uassert_int_equal(state.ioctl_calls, 0); + + memset(&state, 0, sizeof(state)); + state.fail_copy_to = 1; + state.output_value = 41; + rc = lwp_ptmx_ioctl_forward(TIOCMGET, &value, tty_ptmx_copy_from, + tty_ptmx_copy_to, tty_ptmx_forward_ioctl, &state); + uassert_int_equal(rc, -EFAULT); + uassert_int_equal(state.copy_to_calls, 1); + uassert_int_equal(state.ioctl_calls, 1); + + memset(&state, 0, sizeof(state)); + rc = lwp_ptmx_ioctl_forward(0xffffffffU, &value, tty_ptmx_copy_from, + tty_ptmx_copy_to, tty_ptmx_forward_ioctl, &state); + uassert_int_equal(rc, -ENOTTY); + uassert_int_equal(state.copy_from_calls, 0); + uassert_int_equal(state.copy_to_calls, 0); + uassert_int_equal(state.ioctl_calls, 0); +} + static rt_err_t utest_tc_init(void) { return RT_EOK; @@ -102,5 +251,7 @@ static rt_err_t utest_tc_cleanup(void) static void testcase(void) { UTEST_UNIT_RUN(tty_ptmx_buffer_bounds_tc); + UTEST_UNIT_RUN(tty_ptmx_ioctl_arg_type_tc); + UTEST_UNIT_RUN(tty_ptmx_ioctl_forward_tc); } -UTEST_TC_EXPORT(testcase, "testcases.lwp.tty_ptmx.buffer_bounds", utest_tc_init, utest_tc_cleanup, 10); +UTEST_TC_EXPORT(testcase, "testcases.lwp.tty_ptmx.regression", utest_tc_init, utest_tc_cleanup, 10); |
