work around wrong kernel type for sem_nsems member of struct semid_ds
authorRich Felker <dalias@aerifal.cx>
Sat, 29 Jun 2013 03:57:58 +0000 (23:57 -0400)
committerRich Felker <dalias@aerifal.cx>
Sat, 29 Jun 2013 03:57:58 +0000 (23:57 -0400)
rejecting invalid values for n is fine even in the case where a new
sem will not be created, since the kernel does its range checks on n
even in this case as well.

by default, the kernel will bound the limit well below USHRT_MAX
anyway, but it's presumably possible that an administrator could
override this limit and break things.

include/sys/sem.h
src/ipc/semget.c

index cc3a3e6..e74ea20 100644 (file)
@@ -25,13 +25,21 @@ extern "C" {
 #define SETVAL         16
 #define SETALL         17
 
 #define SETVAL         16
 #define SETALL         17
 
+#include <endian.h>
+
 struct semid_ds {
        struct ipc_perm sem_perm;
        long sem_otime;
        unsigned long __unused1;
        long sem_ctime;
        unsigned long __unused2;
 struct semid_ds {
        struct ipc_perm sem_perm;
        long sem_otime;
        unsigned long __unused1;
        long sem_ctime;
        unsigned long __unused2;
-       unsigned long sem_nsems;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       unsigned short sem_nsems;
+       char __sem_nsems_pad[sizeof(long)-sizeof(short)];
+#else
+       char __sem_nsems_pad[sizeof(long)-sizeof(short)];
+       unsigned short sem_nsems;
+#endif
        unsigned long __unused3;
        unsigned long __unused4;
 };
        unsigned long __unused3;
        unsigned long __unused4;
 };
index 5f110e3..c4a559d 100644 (file)
@@ -1,9 +1,16 @@
 #include <sys/sem.h>
 #include <sys/sem.h>
+#include <limits.h>
+#include <errno.h>
 #include "syscall.h"
 #include "ipc.h"
 
 int semget(key_t key, int n, int fl)
 {
 #include "syscall.h"
 #include "ipc.h"
 
 int semget(key_t key, int n, int fl)
 {
+       /* The kernel uses the wrong type for the sem_nsems member
+        * of struct semid_ds, and thus might not check that the
+        * n fits in the correct (per POSIX) userspace type, so
+        * we have to check here. */
+       if (n > USHRT_MAX) return __syscall_ret(-EINVAL);
 #ifdef SYS_semget
        return syscall(SYS_semget, key, n, fl);
 #else
 #ifdef SYS_semget
        return syscall(SYS_semget, key, n, fl);
 #else