Blob Blame History Raw
From 25e40facc12017de2c4df5792f6a0a3f7db39896 Mon Sep 17 00:00:00 2001
From: Eugene Syromyatnikov <evgsyr@gmail.com>
Date: Tue, 29 Jan 2019 14:40:11 +0100
Subject: [PATCH 18/27] Use accessors for tcp->s_ent, return a stub struct if
 it is NULL

Since code paths are non-trivial, it's an attempt to future-proof
and prevent improper access of tcp->s_ent fields.

* defs.h (struct tcb): Update the description of s_ent field.
(stub_sysent): New declaration.
(tcp_sysent, n_args): New macro functions.
(indirect_ipccall): Use tcp_sysent() instead of tcp->s_ent.
* ipc.c (SYS_FUNC(ipc)): Use n_args() instead of tcp->s_ent->nargs.
* linux/alpha/get_syscall_args.c (get_syscall_args): Likewise.
* linux/bfin/get_syscall_args.c (get_syscall_args): Likewise.
* linux/hppa/get_syscall_args.c (get_syscall_args): Likewise.
* linux/ia64/get_syscall_args.c (get_syscall_args): Likewise.
* linux/microblaze/get_syscall_args.c (get_syscall_args): Likewise.
* linux/mips/get_syscall_args.c (get_syscall_args): Likewise.
* linux/sh/get_syscall_args.c (get_syscall_args): Likewise.
* linux/sh64/get_syscall_args.c (get_syscall_args): Likewise.
* linux/x86_64/get_syscall_args.c (get_syscall_args): Likewise.
* linux/xtensa/get_syscall_args.c (get_syscall_args): Likewise.
* prctl.c (print_prctl_args): Likewise.
* signal.c (SYS_FUNC(sgetmask)): Likewise.
* util.c (printargs, printargs_u, printargs_d): Likewise.
* syscall.c (decode_ipc_subcall, decode_syscall_subcall: Likewise.
(dumpio, tamper_with_syscall_exiting, syscall_entering_decode,
syscall_entering_decode, syscall_entering_trace, syscall_entering_trace,
syscall_exiting_decode, print_syscall_resume, syscall_exiting_trace,
get_syscall_result): Use tcp_sysent() instead of tcp->s_ent.
(stub_sysent): New stub sysent.
(get_scno): Reset scno, s_ent, qual_flg; initialise s->ent from
stub_sysent.
* pathtrace.c (pathtrace_match_set): Use tcp_sysent() instead of
tcp->s_ent.

Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>

Conflicts:
	defs.h
	linux/mips/get_syscall_args.c
	syscall.c
---
 defs.h                              | 10 ++++++--
 ipc.c                               |  2 +-
 linux/alpha/get_syscall_args.c      |  2 +-
 linux/bfin/get_syscall_args.c       |  2 +-
 linux/hppa/get_syscall_args.c       |  2 +-
 linux/ia64/get_syscall_args.c       |  2 +-
 linux/microblaze/get_syscall_args.c |  2 +-
 linux/mips/get_syscall_args.c       |  6 ++---
 linux/sh/get_syscall_args.c         |  2 +-
 linux/sh64/get_syscall_args.c       |  2 +-
 linux/x86_64/get_syscall_args.c     |  2 +-
 linux/xtensa/get_syscall_args.c     |  2 +-
 pathtrace.c                         |  2 +-
 prctl.c                             |  2 +-
 signal.c                            |  4 +--
 syscall.c                           | 49 +++++++++++++++++++++----------------
 util.c                              |  6 ++---
 17 files changed, 56 insertions(+), 43 deletions(-)

diff --git a/defs.h b/defs.h
index c7274c8..811bb0d 100644
--- a/defs.h
+++ b/defs.h
@@ -206,7 +206,9 @@ struct tcb {
 	const char *auxstr;	/* Auxiliary info from syscall (see RVAL_STR) */
 	void *_priv_data;	/* Private data for syscall decoding functions */
 	void (*_free_priv_data)(void *); /* Callback for freeing priv_data */
-	const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad scno */
+	const struct_sysent *s_ent; /* sysent[scno] or a stub struct for bad
+				     * scno.  Use tcp_sysent() macro for access.
+				     */
 	const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" msg */
 	struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
 	struct timespec stime;	/* System time usage as of last process wait */
@@ -283,6 +285,10 @@ struct tcb {
 #define syscall_delayed(tcp)	((tcp)->flags & TCB_DELAYED)
 #define syscall_tampered_nofail(tcp) ((tcp)->flags & TCB_TAMPERED_NO_FAIL)
 
+extern const struct_sysent stub_sysent;
+#define tcp_sysent(tcp) (tcp->s_ent ?: &stub_sysent)
+#define n_args(tcp) (tcp_sysent(tcp)->nargs)
+
 #include "xlat.h"
 
 extern const struct xlat addrfams[];
@@ -352,7 +358,7 @@ extern const struct xlat whence_codes[];
 #define IOCTL_NUMBER_HANDLED 1
 #define IOCTL_NUMBER_STOP_LOOKUP 010
 
-#define indirect_ipccall(tcp) (tcp->s_ent->sys_flags & TRACE_INDIRECT_SUBCALL)
+#define indirect_ipccall(tcp) (tcp_sysent(tcp)->sys_flags & TRACE_INDIRECT_SUBCALL)
 
 enum sock_proto {
 	SOCK_PROTO_UNKNOWN,
diff --git a/ipc.c b/ipc.c
index c35509b..b2b3aa4 100644
--- a/ipc.c
+++ b/ipc.c
@@ -21,7 +21,7 @@ SYS_FUNC(ipc)
 	printxval_u(ipccalls, call, NULL);
 
 	unsigned int i;
-	for (i = 1; i < tcp->s_ent->nargs; ++i)
+	for (i = 1; i < n_args(tcp); ++i)
 		tprintf(", %#" PRI_klx, tcp->u_arg[i]);
 
 	return RVAL_DECODED;
diff --git a/linux/alpha/get_syscall_args.c b/linux/alpha/get_syscall_args.c
index be61abf..c28eb62 100644
--- a/linux/alpha/get_syscall_args.c
+++ b/linux/alpha/get_syscall_args.c
@@ -4,7 +4,7 @@ get_syscall_args(struct tcb *tcp)
 {
 	unsigned int i;
 
-	for (i = 0; i < tcp->s_ent->nargs; ++i)
+	for (i = 0; i < n_args(tcp); ++i)
 		if (upeek(tcp, REG_A0+i, &tcp->u_arg[i]) < 0)
 			return -1;
 	return 1;
diff --git a/linux/bfin/get_syscall_args.c b/linux/bfin/get_syscall_args.c
index 506d3a9..bebaf03 100644
--- a/linux/bfin/get_syscall_args.c
+++ b/linux/bfin/get_syscall_args.c
@@ -7,7 +7,7 @@ get_syscall_args(struct tcb *tcp)
 	};
 	unsigned int i;
 
-	for (i = 0; i < tcp->s_ent->nargs; ++i)
+	for (i = 0; i < n_args(tcp); ++i)
 		if (upeek(tcp, argreg[i], &tcp->u_arg[i]) < 0)
 			return -1;
 	return 1;
diff --git a/linux/hppa/get_syscall_args.c b/linux/hppa/get_syscall_args.c
index aa938f5..70f2a24 100644
--- a/linux/hppa/get_syscall_args.c
+++ b/linux/hppa/get_syscall_args.c
@@ -4,7 +4,7 @@ get_syscall_args(struct tcb *tcp)
 {
 	unsigned int i;
 
-	for (i = 0; i < tcp->s_ent->nargs; ++i)
+	for (i = 0; i < n_args(tcp); ++i)
 		if (upeek(tcp, PT_GR26-4*i, &tcp->u_arg[i]) < 0)
 			return -1;
 	return 1;
diff --git a/linux/ia64/get_syscall_args.c b/linux/ia64/get_syscall_args.c
index ad73c54..eeeb59c 100644
--- a/linux/ia64/get_syscall_args.c
+++ b/linux/ia64/get_syscall_args.c
@@ -11,7 +11,7 @@ get_syscall_args(struct tcb *tcp)
 	unsigned long *out0 = ia64_rse_skip_regs(rbs_end, -sof + sol);
 	unsigned int i;
 
-	for (i = 0; i < tcp->s_ent->nargs; ++i) {
+	for (i = 0; i < n_args(tcp); ++i) {
 		if (umove(tcp,
 			  (unsigned long) ia64_rse_skip_regs(out0, i),
 			  &tcp->u_arg[i]) < 0) {
diff --git a/linux/microblaze/get_syscall_args.c b/linux/microblaze/get_syscall_args.c
index 1a3a48f..0e249e3 100644
--- a/linux/microblaze/get_syscall_args.c
+++ b/linux/microblaze/get_syscall_args.c
@@ -4,7 +4,7 @@ get_syscall_args(struct tcb *tcp)
 {
 	unsigned int i;
 
-	for (i = 0; i < tcp->s_ent->nargs; ++i)
+	for (i = 0; i < n_args(tcp); ++i)
 		if (upeek(tcp, (5 + i) * 4, &tcp->u_arg[i]) < 0)
 			return -1;
 	return 1;
diff --git a/linux/mips/get_syscall_args.c b/linux/mips/get_syscall_args.c
index 3c9160e..0ff5136 100644
--- a/linux/mips/get_syscall_args.c
+++ b/linux/mips/get_syscall_args.c
@@ -14,16 +14,16 @@ get_syscall_args(struct tcb *tcp)
 	tcp->u_arg[1] = mips_REG_A1;
 	tcp->u_arg[2] = mips_REG_A2;
 	tcp->u_arg[3] = mips_REG_A3;
-	if (tcp->s_ent->nargs > 4
+	if (n_args(tcp) > 4
 	    && umoven(tcp, mips_REG_SP + 4 * sizeof(tcp->u_arg[0]),
-		      (tcp->s_ent->nargs - 4) * sizeof(tcp->u_arg[0]),
+		      (n_args(tcp) - 4) * sizeof(tcp->u_arg[0]),
 		      &tcp->u_arg[4]) < 0) {
 		/*
 		 * Let's proceed with the first 4 arguments
 		 * instead of reporting the failure.
 		 */
 		memset(&tcp->u_arg[4], 0,
-		       (tcp->s_ent->nargs - 4) * sizeof(tcp->u_arg[0]));
+		       (n_args(tcp) - 4) * sizeof(tcp->u_arg[0]));
 	}
 #else
 # error unsupported mips abi
diff --git a/linux/sh/get_syscall_args.c b/linux/sh/get_syscall_args.c
index b4ee1dd..02593b0 100644
--- a/linux/sh/get_syscall_args.c
+++ b/linux/sh/get_syscall_args.c
@@ -12,7 +12,7 @@ get_syscall_args(struct tcb *tcp)
 	};
 	unsigned int i;
 
-	for (i = 0; i < tcp->s_ent->nargs; ++i)
+	for (i = 0; i < n_args(tcp); ++i)
 		if (upeek(tcp, syscall_regs[i], &tcp->u_arg[i]) < 0)
 			return -1;
 	return 1;
diff --git a/linux/sh64/get_syscall_args.c b/linux/sh64/get_syscall_args.c
index e35c350..241ff09 100644
--- a/linux/sh64/get_syscall_args.c
+++ b/linux/sh64/get_syscall_args.c
@@ -6,7 +6,7 @@ get_syscall_args(struct tcb *tcp)
 	static const int syscall_regs[MAX_ARGS] = { 2, 3, 4, 5, 6, 7 };
 	unsigned int i;
 
-	for (i = 0; i < tcp->s_ent->nargs; ++i)
+	for (i = 0; i < n_args(tcp); ++i)
 		if (upeek(tcp, REG_GENERAL(syscall_regs[i]),
 			  &tcp->u_arg[i]) < 0)
 			return -1;
diff --git a/linux/x86_64/get_syscall_args.c b/linux/x86_64/get_syscall_args.c
index f285ac3..8ee8ebd 100644
--- a/linux/x86_64/get_syscall_args.c
+++ b/linux/x86_64/get_syscall_args.c
@@ -4,7 +4,7 @@ get_syscall_args(struct tcb *tcp)
 {
 	if (x86_io.iov_len != sizeof(i386_regs)) {
 		/* x86-64 or x32 ABI */
-		if (tcp->s_ent->sys_flags & COMPAT_SYSCALL_TYPES) {
+		if (tcp_sysent(tcp)->sys_flags & COMPAT_SYSCALL_TYPES) {
 			/*
 			 * X32 compat syscall: zero-extend from 32 bits.
 			 * Use truncate_klong_to_current_wordsize(tcp->u_arg[N])
diff --git a/linux/xtensa/get_syscall_args.c b/linux/xtensa/get_syscall_args.c
index 2a20cdb..6903221 100644
--- a/linux/xtensa/get_syscall_args.c
+++ b/linux/xtensa/get_syscall_args.c
@@ -13,7 +13,7 @@ get_syscall_args(struct tcb *tcp)
 	};
 	unsigned int i;
 
-	for (i = 0; i < tcp->s_ent->nargs; ++i)
+	for (i = 0; i < n_args(tcp); ++i)
 		if (upeek(tcp, xtensaregs[i], &tcp->u_arg[i]) < 0)
 			return -1;
 	return 1;
diff --git a/pathtrace.c b/pathtrace.c
index 2cd12e6..5493028 100644
--- a/pathtrace.c
+++ b/pathtrace.c
@@ -167,7 +167,7 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
 {
 	const struct_sysent *s;
 
-	s = tcp->s_ent;
+	s = tcp_sysent(tcp);
 
 	if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
 		return false;
diff --git a/prctl.c b/prctl.c
index 5830f11..c31ea60 100644
--- a/prctl.c
+++ b/prctl.c
@@ -61,7 +61,7 @@ print_prctl_args(struct tcb *tcp, const unsigned int first)
 {
 	unsigned int i;
 
-	for (i = first; i < tcp->s_ent->nargs; ++i)
+	for (i = first; i < n_args(tcp); ++i)
 		tprintf(", %#" PRI_klx, tcp->u_arg[i]);
 }
 
diff --git a/signal.c b/signal.c
index 17a3f79..e59a76d 100644
--- a/signal.c
+++ b/signal.c
@@ -383,10 +383,10 @@ SYS_FUNC(sgetmask)
 SYS_FUNC(sigsuspend)
 {
 #ifdef MIPS
-	print_sigset_addr_len(tcp, tcp->u_arg[tcp->s_ent->nargs - 1],
+	print_sigset_addr_len(tcp, tcp->u_arg[n_args(tcp) - 1],
 			      current_wordsize);
 #else
-	tprint_old_sigmask_val("", tcp->u_arg[tcp->s_ent->nargs - 1]);
+	tprint_old_sigmask_val("", tcp->u_arg[n_args(tcp) - 1]);
 #endif
 
 	return RVAL_DECODED;
diff --git a/syscall.c b/syscall.c
index 33f6dcd..40fcedb 100644
--- a/syscall.c
+++ b/syscall.c
@@ -339,7 +339,7 @@ decode_ipc_subcall(struct tcb *tcp)
 	tcp->qual_flg = qual_flags(tcp->scno);
 	tcp->s_ent = &sysent[tcp->scno];
 
-	const unsigned int n = tcp->s_ent->nargs;
+	const unsigned int n = n_args(tcp);
 	unsigned int i;
 	for (i = 0; i < n; i++)
 		tcp->u_arg[i] = tcp->u_arg[i + 1];
@@ -363,7 +363,7 @@ decode_syscall_subcall(struct tcb *tcp)
 	 * and sync_file_range) requires additional code,
 	 * see linux/mips/get_syscall_args.c
 	 */
-	if (tcp->s_ent->nargs == MAX_ARGS) {
+	if (n_args(tcp) == MAX_ARGS) {
 		if (umoven(tcp,
 			   mips_REG_SP + MAX_ARGS * sizeof(tcp->u_arg[0]),
 			   sizeof(tcp->u_arg[0]),
@@ -382,7 +382,7 @@ dumpio(struct tcb *tcp)
 		return;
 
 	if (is_number_in_set(fd, write_set)) {
-		switch (tcp->s_ent->sen) {
+		switch (tcp_sysent(tcp)->sen) {
 		case SEN_write:
 		case SEN_pwrite:
 		case SEN_send:
@@ -409,7 +409,7 @@ dumpio(struct tcb *tcp)
 		return;
 
 	if (is_number_in_set(fd, read_set)) {
-		switch (tcp->s_ent->sen) {
+		switch (tcp_sysent(tcp)->sen) {
 		case SEN_read:
 		case SEN_pread:
 		case SEN_recv:
@@ -573,7 +573,7 @@ tamper_with_syscall_exiting(struct tcb *tcp)
 
 	if (update_tcb) {
 		tcp->u_error = 0;
-		get_error(tcp, !(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS));
+		get_error(tcp, !(tcp_sysent(tcp)->sys_flags & SYSCALL_NEVER_FAILS));
 	}
 
 	return 0;
@@ -593,10 +593,9 @@ syscall_entering_decode(struct tcb *tcp)
 	int res = get_scno(tcp);
 	if (res == 0)
 		return res;
-	int scno_good = res;
 	if (res != 1 || (res = get_syscall_args(tcp)) != 1) {
 		printleader(tcp);
-		tprintf("%s(", scno_good == 1 ? tcp->s_ent->sys_name : "????");
+		tprintf("%s(", tcp_sysent(tcp)->sys_name);
 		/*
 		 * " <unavailable>" will be added later by the code which
 		 * detects ptrace errors.
@@ -608,7 +607,7 @@ syscall_entering_decode(struct tcb *tcp)
  || defined SYS_socket_subcall	\
  || defined SYS_syscall_subcall
 	for (;;) {
-		switch (tcp->s_ent->sen) {
+		switch (tcp_sysent(tcp)->sen) {
 # ifdef SYS_ipc_subcall
 		case SEN_ipc:
 			decode_ipc_subcall(tcp);
@@ -622,7 +621,7 @@ syscall_entering_decode(struct tcb *tcp)
 # ifdef SYS_syscall_subcall
 		case SEN_syscall:
 			decode_syscall_subcall(tcp);
-			if (tcp->s_ent->sen != SEN_syscall)
+			if (tcp_sysent(tcp)->sen != SEN_syscall)
 				continue;
 			break;
 # endif
@@ -642,7 +641,7 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
 		tcp->qual_flg &= ~QUAL_INJECT;
 	}
 
-	switch (tcp->s_ent->sen) {
+	switch (tcp_sysent(tcp)->sen) {
 		case SEN_execve:
 		case SEN_execveat:
 #if defined SPARC || defined SPARC64
@@ -672,14 +671,14 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
 
 #ifdef ENABLE_STACKTRACE
 	if (stack_trace_enabled) {
-		if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
+		if (tcp_sysent(tcp)->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
 			unwind_tcb_capture(tcp);
 	}
 #endif
 
 	printleader(tcp);
-	tprintf("%s(", tcp->s_ent->sys_name);
-	int res = raw(tcp) ? printargs(tcp) : tcp->s_ent->sys_func(tcp);
+	tprintf("%s(", tcp_sysent(tcp)->sys_name);
+	int res = raw(tcp) ? printargs(tcp) : tcp_sysent(tcp)->sys_func(tcp);
 	fflush(tcp->outf);
 	return res;
 }
@@ -709,7 +708,7 @@ syscall_exiting_decode(struct tcb *tcp, struct timespec *pts)
 	if ((Tflag || cflag) && !(filtered(tcp) || hide_log(tcp)))
 		clock_gettime(CLOCK_MONOTONIC, pts);
 
-	if (tcp->s_ent->sys_flags & MEMORY_MAPPING_CHANGE)
+	if (tcp_sysent(tcp)->sys_flags & MEMORY_MAPPING_CHANGE)
 		mmap_notify_report(tcp);
 
 	if (filtered(tcp) || hide_log(tcp))
@@ -738,7 +737,7 @@ print_syscall_resume(struct tcb *tcp)
 	    || (tcp->flags & TCB_REPRINT)) {
 		tcp->flags &= ~TCB_REPRINT;
 		printleader(tcp);
-		tprintf("<... %s resumed>", tcp->s_ent->sys_name);
+		tprintf("<... %s resumed>", tcp_sysent(tcp)->sys_name);
 	}
 }
 
@@ -786,7 +785,7 @@ syscall_exiting_trace(struct tcb *tcp, struct timespec *ts, int res)
 		if (tcp->sys_func_rval & RVAL_DECODED)
 			sys_res = tcp->sys_func_rval;
 		else
-			sys_res = tcp->s_ent->sys_func(tcp);
+			sys_res = tcp_sysent(tcp)->sys_func(tcp);
 	}
 
 	tprints(") ");
@@ -1175,6 +1174,13 @@ free_sysent_buf(void *ptr)
 	free(ptr);
 }
 
+const struct_sysent stub_sysent = {
+	.nargs = MAX_ARGS,
+	.sen = SEN_printargs,
+	.sys_func = printargs,
+	.sys_name = "????",
+};
+
 /*
  * Returns:
  * 0: "ignore this ptrace stop", syscall_entering_decode() should return a "bail
@@ -1186,6 +1192,10 @@ free_sysent_buf(void *ptr)
 int
 get_scno(struct tcb *tcp)
 {
+	tcp->scno = -1;
+	tcp->s_ent = NULL;
+	tcp->qual_flg = QUAL_RAW | DEFAULT_QUAL_FLAGS;
+
 	if (get_regs(tcp) < 0)
 		return -1;
 
@@ -1202,14 +1212,11 @@ get_scno(struct tcb *tcp)
 		struct sysent_buf *s = xcalloc(1, sizeof(*s));
 
 		s->tcp = tcp;
-		s->ent.nargs = MAX_ARGS;
-		s->ent.sen = SEN_printargs;
-		s->ent.sys_func = printargs;
+		s->ent = stub_sysent;
 		s->ent.sys_name = s->buf;
 		xsprintf(s->buf, "syscall_%#" PRI_klx, shuffle_scno(tcp->scno));
 
 		tcp->s_ent = &s->ent;
-		tcp->qual_flg = QUAL_RAW | DEFAULT_QUAL_FLAGS;
 
 		set_tcb_priv_data(tcp, s, free_sysent_buf);
 
@@ -1246,7 +1253,7 @@ get_syscall_result(struct tcb *tcp)
 		return -1;
 	tcp->u_error = 0;
 	get_error(tcp,
-		  (!(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)
+		  (!(tcp_sysent(tcp)->sys_flags & SYSCALL_NEVER_FAILS)
 			|| syscall_tampered(tcp))
                   && !syscall_tampered_nofail(tcp));
 
diff --git a/util.c b/util.c
index aefedee..b841c0f 100644
--- a/util.c
+++ b/util.c
@@ -1161,7 +1161,7 @@ print_array_ex(struct tcb *const tcp,
 int
 printargs(struct tcb *tcp)
 {
-	const int n = tcp->s_ent->nargs;
+	const int n = n_args(tcp);
 	int i;
 	for (i = 0; i < n; ++i)
 		tprintf("%s%#" PRI_klx, i ? ", " : "", tcp->u_arg[i]);
@@ -1171,7 +1171,7 @@ printargs(struct tcb *tcp)
 int
 printargs_u(struct tcb *tcp)
 {
-	const int n = tcp->s_ent->nargs;
+	const int n = n_args(tcp);
 	int i;
 	for (i = 0; i < n; ++i)
 		tprintf("%s%u", i ? ", " : "",
@@ -1182,7 +1182,7 @@ printargs_u(struct tcb *tcp)
 int
 printargs_d(struct tcb *tcp)
 {
-	const int n = tcp->s_ent->nargs;
+	const int n = n_args(tcp);
 	int i;
 	for (i = 0; i < n; ++i)
 		tprintf("%s%d", i ? ", " : "",
-- 
2.1.4