[Chicken-users] [SECURITY] Buffer overrun vulnerability in Chicken's scheduler

2012-06-11 Thread Peter Bex
Hello Chicken users,

Recently a buffer overrun error was discovered in Chicken's thread
scheduler.  This buffer overrun is triggered on UNIX-like OSes when a
file descriptor with an integer value higher than FD_SETSIZE gets
opened due to the way the POSIX select() function is currently being
used.

Every Chicken program which accepts a potentially unlimited number of
incoming network connections or otherwise opens an unlimited number of
file descriptors is potentially vulnerable to an application crash.

Currently a patch is being developed.  In the meanwhile an effective
workaround is to limit the maximum number of open descriptors using
the Unix ulimit -n command.

Please verify the maximum number of descriptors supported safely by
Chicken by compiling following one-line program using csc and then
running the resulting binary:

(print (foreign-value FD_SETSIZE int))

Simply ensure that the programs you want to protect run under a maximum
number of open files ulimit that matches this number (or lower).

lease note that this bug also affects people using the
high-load-scheduler egg, which provides an alternative scheduler
implementation.

On Windows, there is no buffer overrun danger, but there is a potential
problem of threads never waking up; there is currently no known
workaround for this.

References
--

https://lists.ubuntu.com/archives/ubuntu-devel/2010-September/031446.html
http://sourceware.org/bugzilla/show_bug.cgi?id=10352
http://pubs.opengroup.org/onlinepubs/007908799/xsh/poll.html
http://netbsd.gw.com/cgi-bin/man-cgi?select++NetBSD-current

Kind regards,
The Chicken team

___
Chicken-users mailing list
Chicken-users@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-users


Re: [Chicken-users] [SECURITY] Buffer overrun vulnerability in Chicken's scheduler

2012-06-11 Thread Peter Bex
On Mon, Jun 11, 2012 at 12:33:28PM +0200, Peter Bex wrote:
 Currently a patch is being developed.  In the meanwhile an effective
 workaround is to limit the maximum number of open descriptors using
 the Unix ulimit -n command.

For those feeling adventurous, please test the attached patch.  We are
currently debating whether it should be applied, but unsure because
there could be some potential portability problems with it as well as
the fact that it's a reasonably large change to a core component of
Chicken which is pretty subtle.

It would be especially useful if people using anything *other* than
Linux or BSD could report back success or failure.

Cheers,
Peter
-- 
http://sjamaan.ath.cx
--
The process of preparing programs for a digital computer
 is especially attractive, not only because it can be economically
 and scientifically rewarding, but also because it can be an aesthetic
 experience much like composing poetry or music.
-- Donald Knuth
From 52e581254a713daa2f19693108af35a859ec7ebd Mon Sep 17 00:00:00 2001
From: Peter Bex peter@xs4all.nl
Date: Fri, 8 Jun 2012 22:08:40 +0200
Subject: [PATCH] Fix scheduler so it can handle file descriptors over
 FD_SETSIZE (which occurs in practice on systems where
 ulimit -n has a higher value)

---
 NEWS  |1 +
 scheduler.scm |   98 ++--
 2 files changed, 60 insertions(+), 39 deletions(-)

diff --git a/NEWS b/NEWS
index 7ec6431..725dddc 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,7 @@
   - on 64-bit machines the random procedure no longer truncates result
 values (which caused very nonrandom results for very large values).
 Note that random shouldn't be used for security-critical code.
+  - Fixed select()-calls to accommodate more than FD_SETSIZE open files.
 
 - Build system
   - version information has been moved into a separate unit to make the
diff --git a/scheduler.scm b/scheduler.scm
index e3a96bc..c28ed16 100644
--- a/scheduler.scm
+++ b/scheduler.scm
@@ -31,9 +31,8 @@
   (hide ready-queue-head ready-queue-tail ##sys#timeout-list
##sys#update-thread-state-buffer ##sys#restore-thread-state-buffer
remove-from-ready-queue ##sys#unblock-threads-for-i/o 
##sys#force-primordial
-   fdset-input-set fdset-output-set fdset-clear
-   fdset-select-timeout fdset-set fdset-test
-   create-fdset stderr
+   current-fdset-size fd-iset fd-oset fdset-extend fdset-clear make-fdset
+fdset-select-timeout fdset-set fdset-test create-fdset stderr
##sys#clear-i/o-state-for-thread! ##sys#abandon-mutexes) 
   (not inline ##sys#interrupt-hook)
   (unsafe)
@@ -74,9 +73,16 @@ C_word C_msleep(C_word ms) {
   return C_SCHEME_TRUE;
 }
 #endif
+/* These two should be defined by select.h, but are not standardized... */
+#ifndef howmany
+#definehowmany(x, y)   (((x)+((y)-1))/(y))
+#endif
+#ifndef NFDBITS
+#define NFDBITS (sizeof (fd_mask) * 8)
+#endif
+
 static fd_set C_fdset_input, C_fdset_output;
-#define C_fd_test_input(fd)  C_mk_bool(FD_ISSET(C_unfix(fd), C_fdset_input))
-#define C_fd_test_output(fd)  C_mk_bool(FD_ISSET(C_unfix(fd), C_fdset_output))
+#define C_fd_test(fd,set)  C_mk_bool(FD_ISSET(C_unfix(fd), (fd_set 
*)C_data_pointer(set)))
 EOF
 ) )
 
@@ -344,46 +350,61 @@ EOF
 
 (define ##sys#fd-list '()) ; ((FD1 THREAD1 ...) ...)
 
+(define current-fdset-size 64)
+(define fd-iset (make-fdset current-fdset-size))
+(define fd-oset (make-fdset current-fdset-size))
+
 (define (create-fdset)
-  (fdset-clear)
-  (let loop ((lst ##sys#fd-list))
-(unless (null? lst)
-  (let ((fd (caar lst)))
-   (for-each
-(lambda (t)
-  (let ((p (##sys#slot t 11)))
-(fdset-set fd (cdr p
-(cdar lst))
-   (loop (cdr lst))
+  (let* ((max (foldl (lambda (max x) (fxmax max (car x))) 0 ##sys#fd-list))
+(size (let lp ((s current-fdset-size)) ; Find next power of two
+(if (fx max s) (lp (fxshl s 1)) s
+(when (fx size current-fdset-size) ; Adjust set sizes if neccessary
+  (set! current-fdset-size size)
+  (set! fd-iset (make-fdset size))
+  (set! fd-oset (make-fdset size)))
+(fdset-clear fd-iset)
+(fdset-clear fd-oset)
+(let loop ((lst ##sys#fd-list))
+  (unless (null? lst)
+   (let ((fd (caar lst)))
+ (for-each
+  (lambda (t)
+(let ((p (##sys#slot t 11)))
+  (fdset-set fd (cdr p
+  (cdar lst))
+ (loop (cdr lst)
+max))
 
 (define fdset-select-timeout
-  (foreign-lambda* int ([bool to] [double tm])
+  (foreign-lambda* int ((int maxfd) (bool to) (double tm)
+(scheme-pointer iset) (scheme-pointer oset))
 struct timeval timeout;
 timeout.tv_sec = tm / 1000;
 timeout.tv_usec = fmod(tm, 1000) * 1000;
-C_return(select(FD_SETSIZE, C_fdset_input, C_fdset_output, NULL, to ? 
timeout : NULL));) )
+