This is the mail archive of the libc-hacker@sources.redhat.com mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] sparc-linux semctl fix


Hi!

The following patch fixes semctl on sparc32.
The problem is that on arches which pass unions (whatever size) indirectly,
if e.g. semctl (semid, 0, IPC_RMID); is called, then it segfaults, while
would work if semctl (semid, 0, IPC_RMID, (union semun) { 0 } ); was called
(which is not required by standards though).
I think PPC and maybe some other arches which pass word-size unions to
vararg functions indirectly need the same fix (but AFAIC ppc
uses the generic linux semctl.c, not i386 one, so needs to have its own
copy again; or maybe the generic code should
have something which works everywhere and architectures which can optimize
could optimize). It works on i386 because unions are passed directly on the
stack, it works on sparc64 and ia64 because unions are passed directly
in registers (in sparc64 case actually only unions <= 2*wordsize).

2002-04-26  Jakub Jelinek  <jakub@redhat.com>

	* sysdeps/unix/sysv/linux/sparc/sparc32/semctl.c: Copied from
	i386/semctl.c.
	(__old_semctl, __new_semctl): Only use va_arg if the argument will
	be used.

--- libc/sysdeps/unix/sysv/linux/sparc/sparc32/semctl.c.jj	Fri Feb 11 20:44:42 2000
+++ libc/sysdeps/unix/sysv/linux/sparc/sparc32/semctl.c	Thu Apr 25 22:52:00 2002
@@ -1 +1,205 @@
-#include <sysdeps/unix/sysv/linux/i386/semctl.c>
+/* Semctl for architectures where word sized unions are passed indirectly
+   Copyright (C) 1995, 1997, 1998, 2000, 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, August 1995.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/sem.h>
+#include <ipc_priv.h>
+
+#include <sysdep.h>
+#include <string.h>
+#include <sys/syscall.h>
+
+#include "kernel-features.h"
+#include <shlib-compat.h>
+
+struct __old_semid_ds
+{
+  struct __old_ipc_perm sem_perm;	/* operation permission struct */
+  __time_t sem_otime;			/* last semop() time */
+  __time_t sem_ctime;			/* last time changed by semctl() */
+  struct sem *__sembase;		/* ptr to first semaphore in array */
+  struct sem_queue *__sem_pending;	/* pending operations */
+  struct sem_queue *__sem_pending_last; /* last pending operation */
+  struct sem_undo *__undo;		/* ondo requests on this array */
+  unsigned short int sem_nsems;		/* number of semaphores in set */
+};
+
+/* Define a `union semun' suitable for Linux here.  */
+union semun
+{
+  int val;			/* value for SETVAL */
+  struct semid_ds *buf;		/* buffer for IPC_STAT & IPC_SET */
+  unsigned short int *array;	/* array for GETALL & SETALL */
+  struct seminfo *__buf;	/* buffer for IPC_INFO */
+};
+
+#include <bp-checks.h>
+#include <bp-semctl.h>		/* definition of CHECK_SEMCTL needs union semum */
+
+#ifdef __NR_getuid32
+# if __ASSUME_32BITUIDS == 0
+/* This variable is shared with all files that need to check for 32bit
+   uids.  */
+extern int __libc_missing_32bit_uids;
+# endif
+#endif
+
+/* Return identifier for array of NSEMS semaphores associated with
+   KEY.  */
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
+int __old_semctl (int semid, int semnum, int cmd, ...);
+#endif
+int __new_semctl (int semid, int semnum, int cmd, ...);
+
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
+int
+__old_semctl (int semid, int semnum, int cmd, ...)
+{
+  union semun arg;
+  va_list ap;
+
+  /* Get the argument only if required.  */
+  arg.buf = NULL;
+  switch (cmd)
+    {
+    case SETVAL:	/* arg.val */
+    case GETALL:	/* arg.array */
+    case SETALL:
+    case IPC_STAT:	/* arg.buf */
+    case IPC_SET:
+    case SEM_STAT:
+    case IPC_INFO:	/* arg.__buf */
+    case SEM_INFO:
+      va_start (ap, cmd);
+      arg = va_arg (ap, union semun);
+      va_end (ap);
+      break;
+    }
+
+  return INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd,
+			 CHECK_SEMCTL (&arg, semid, cmd));
+}
+compat_symbol (libc, __old_semctl, semctl, GLIBC_2_0);
+#endif
+
+int
+__new_semctl (int semid, int semnum, int cmd, ...)
+{
+  union semun arg;
+  va_list ap;
+
+  /* Get the argument only if required.  */
+  arg.buf = NULL;
+  switch (cmd)
+    {
+    case SETVAL:	/* arg.val */
+    case GETALL:	/* arg.array */
+    case SETALL:
+    case IPC_STAT:	/* arg.buf */
+    case IPC_SET:
+    case SEM_STAT:
+    case IPC_INFO:	/* arg.__buf */
+    case SEM_INFO:
+      va_start (ap, cmd);
+      arg = va_arg (ap, union semun);
+      va_end (ap);
+      break;
+    }
+
+#if __ASSUME_32BITUIDS > 0
+  return INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
+			 CHECK_SEMCTL (&arg, semid, cmd | __IPC_64));
+#else
+  switch (cmd) {
+    case SEM_STAT:
+    case IPC_STAT:
+    case IPC_SET:
+      break;
+    default:
+      return INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd,
+			     CHECK_SEMCTL (&arg, semid, cmd));
+  }
+
+  {
+    int result;
+    struct __old_semid_ds old;
+    struct semid_ds *buf;
+
+#ifdef __NR_getuid32
+    if (__libc_missing_32bit_uids <= 0)
+      {
+	if (__libc_missing_32bit_uids < 0)
+	  {
+	    int save_errno = errno;
+
+	    /* Test presence of new IPC by testing for getuid32 syscall.  */
+	    result = INLINE_SYSCALL (getuid32, 0);
+	    if (result == -1 && errno == ENOSYS)
+	      __libc_missing_32bit_uids = 1;
+	    else
+	      __libc_missing_32bit_uids = 0;
+	    __set_errno(save_errno);
+	  }
+	if (__libc_missing_32bit_uids <= 0)
+	  {
+	    result = INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
+				     CHECK_SEMCTL (&arg, semid, cmd | __IPC_64));
+	    return result;
+	  }
+      }
+#endif
+
+    buf = arg.buf;
+    arg.buf = (struct semid_ds *)&old;
+    if (cmd == IPC_SET)
+      {
+	old.sem_perm.uid = buf->sem_perm.uid;
+	old.sem_perm.gid = buf->sem_perm.gid;
+	old.sem_perm.mode = buf->sem_perm.mode;
+	if (old.sem_perm.uid != buf->sem_perm.uid ||
+	    old.sem_perm.gid != buf->sem_perm.gid)
+	  {
+	    __set_errno (EINVAL);
+	    return -1;
+	  }
+      }
+    result = INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd,
+			     CHECK_SEMCTL (&arg, semid, cmd));
+    if (result != -1 && cmd != IPC_SET)
+      {
+	memset(buf, 0, sizeof(*buf));
+	buf->sem_perm.__key = old.sem_perm.__key;
+	buf->sem_perm.uid = old.sem_perm.uid;
+	buf->sem_perm.gid = old.sem_perm.gid;
+	buf->sem_perm.cuid = old.sem_perm.cuid;
+	buf->sem_perm.cgid = old.sem_perm.cgid;
+	buf->sem_perm.mode = old.sem_perm.mode;
+	buf->sem_perm.__seq = old.sem_perm.__seq;
+	buf->sem_otime = old.sem_otime;
+	buf->sem_ctime = old.sem_ctime;
+	buf->sem_nsems = old.sem_nsems;
+      }
+    return result;
+  }
+#endif
+}
+
+versioned_symbol (libc, __new_semctl, semctl, GLIBC_2_2);

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]