strace seg with select and -ve nfds
Dr. David Alan Gilbert
dave at treblig.org
Mon Nov 4 21:39:26 UTC 2013
The 'trinity' fuzz tester managed to trigger a seg of strace
when doing a select() with a -ve nfds value but pointing to a valid large
buffer (on x86 Linux).
My patch below fixes this; I'm not 100% happy because:
1) It seems way too complicated - can't we quit earlier when we find
the length is weird?
2) It's odd the way the code reads the arg for fdsize and then later
reads it again for nfds, I think that's really the underlying
reason this tripped.
3) I'd like some reassurance that my understanding of the way
strace's arg types work is right.
(WTH does strace use int for nfds?)
Thoughts?
Dave
commit 9354b400e243cc87f8b2964c4220afcbd077fb30
Author: Dr. David Alan Gilbert <dave at treblig.org>
Date: Mon Nov 4 21:45:24 2013 +0000
Fix select() with -ve nfds
select() with a -ve nfds value but a valid buffer seg'd.
Work with the nfds parameters as an int everywhere (which
it is) and flag when we fixup the value so that we don't then
try and walk the buffer.
Also adds test/select.c to capture the case.
diff --git a/desc.c b/desc.c
index 9bfe4d0..30ff7a7 100644
--- a/desc.c
+++ b/desc.c
@@ -481,24 +481,29 @@ static int
decode_select(struct tcb *tcp, long *args, enum bitness_t bitness)
{
int i, j;
- unsigned nfds, fdsize;
+ int nfds, fdsize;
fd_set *fds;
const char *sep;
long arg;
+ int unusual_nfds = 0;
- fdsize = args[0];
+ fdsize = (int)args[0];
/* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */
- if (args[0] > 1024*1024)
+ if (fdsize > 1024*1024) {
fdsize = 1024*1024;
- if (args[0] < 0)
+ unusual_nfds = 1;
+ }
+ if (fdsize < 0) {
fdsize = 0;
+ unusual_nfds = 1;
+ }
fdsize = (((fdsize + 7) / 8) + sizeof(long)-1) & -sizeof(long);
if (entering(tcp)) {
fds = malloc(fdsize);
if (!fds)
die_out_of_memory();
- nfds = args[0];
+ nfds = (int)args[0];
tprintf("%d", nfds);
for (i = 0; i < 3; i++) {
arg = args[i+1];
@@ -510,7 +515,8 @@ decode_select(struct tcb *tcp, long *args, enum bitness_t bitness)
tprintf(", %#lx", arg);
continue;
}
- if (umoven(tcp, arg, fdsize, (char *) fds) < 0) {
+ if ((umoven(tcp, arg, fdsize, (char *) fds) < 0) ||
+ unusual_nfds) {
tprints(", [?]");
continue;
}
diff --git a/test/.gitignore b/test/.gitignore
index 7eb39cf..c73b64a 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -10,4 +10,5 @@ wait_must_be_interruptible
threaded_execve
mtd
ubi
+select
sigreturn
diff --git a/test/Makefile b/test/Makefile
index 92142b1..cc7d47a 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -3,7 +3,7 @@ CFLAGS += -Wall
PROGS = \
vfork fork sig skodic clone leaderkill childthread \
sigkill_rain wait_must_be_interruptible threaded_execve \
- mtd ubi sigreturn
+ mtd ubi select sigreturn
all: $(PROGS)
diff --git a/test/select.c b/test/select.c
new file mode 100644
index 0000000..fba54b3
--- /dev/null
+++ b/test/select.c
@@ -0,0 +1,31 @@
+/* dave at treblig.org */
+
+#include <sys/select.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BUFSIZE 1024*1024*2
+char buffer[BUFSIZE];
+
+int main ()
+{
+ fd_set rds;
+
+ FD_ZERO(&rds);
+
+ FD_SET(2,&rds);
+ /* Start with a nice simple select */
+ select(3, &rds, &rds, &rds, NULL);
+
+ /* Now the crash case that trinity found, -ve nfds
+ but with a pointer to a large chunk of valid memory */
+ select(-1, (fd_set *)buffer, NULL, NULL,NULL);
+
+ exit(0);
+}
+
--
-----Open up your eyes, open up your mind, open up your code -------
/ Dr. David Alan Gilbert | Running GNU/Linux | Happy \
\ gro.gilbert @ treblig.org | | In Hex /
\ _________________________|_____ http://www.treblig.org |_______/
More information about the Strace-devel
mailing list