9bb5d6
Rewrite of the following commit but adjusted pre-64-bit time_t
9bb5d6
conversion. We want to follow the same upstream behaviour and return
9bb5d6
EINVAL for unknown commands rather than to attempt the command with an
9bb5d6
argument of {0} which has likely never been tested upstream.
9bb5d6
9bb5d6
commit a16d2abd496bd974a88207d5599265aae5ae4880
9bb5d6
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
9bb5d6
Date:   Tue Sep 29 14:29:48 2020 -0300
9bb5d6
9bb5d6
    sysvipc: Return EINVAL for invalid semctl commands
9bb5d6
    
9bb5d6
    It avoids regressions on possible future commands that might require
9bb5d6
    additional libc support.  The downside is new commands added by newer
9bb5d6
    kernels will need further glibc support.
9bb5d6
    
9bb5d6
    Checked on x86_64-linux-gnu and i686-linux-gnu (Linux v4.15 and v5.4).
9bb5d6
9bb5d6
diff --git a/sysdeps/unix/sysv/linux/semctl.c b/sysdeps/unix/sysv/linux/semctl.c
9bb5d6
index bdf31ca7747fe5a4..03c56c69a5412c82 100644
9bb5d6
--- a/sysdeps/unix/sysv/linux/semctl.c
9bb5d6
+++ b/sysdeps/unix/sysv/linux/semctl.c
9bb5d6
@@ -58,6 +58,15 @@ __new_semctl (int semid, int semnum, int cmd, ...)
9bb5d6
       arg = va_arg (ap, union semun);
9bb5d6
       va_end (ap);
9bb5d6
       break;
9bb5d6
+    case IPC_RMID:      /* arg ignored.  */
9bb5d6
+    case GETNCNT:
9bb5d6
+    case GETPID:
9bb5d6
+    case GETVAL:
9bb5d6
+    case GETZCNT:
9bb5d6
+      break;
9bb5d6
+    default:
9bb5d6
+      __set_errno (EINVAL);
9bb5d6
+      return -1;
9bb5d6
     }
9bb5d6
 
9bb5d6
 #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
9bb5d6
diff --git a/sysvipc/test-sysvipc.h b/sysvipc/test-sysvipc.h
9bb5d6
new file mode 100644
9bb5d6
index 0000000000000000..d7ed496511c10afb
9bb5d6
--- /dev/null
9bb5d6
+++ b/sysvipc/test-sysvipc.h
9bb5d6
@@ -0,0 +1,85 @@
9bb5d6
+/* Basic definition for Sysv IPC test functions.
9bb5d6
+   Copyright (C) 2020 Free Software Foundation, Inc.
9bb5d6
+   This file is part of the GNU C Library.
9bb5d6
+
9bb5d6
+   The GNU C Library is free software; you can redistribute it and/or
9bb5d6
+   modify it under the terms of the GNU Lesser General Public
9bb5d6
+   License as published by the Free Software Foundation; either
9bb5d6
+   version 2.1 of the License, or (at your option) any later version.
9bb5d6
+
9bb5d6
+   The GNU C Library is distributed in the hope that it will be useful,
9bb5d6
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
9bb5d6
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9bb5d6
+   Lesser General Public License for more details.
9bb5d6
+
9bb5d6
+   You should have received a copy of the GNU Lesser General Public
9bb5d6
+   License along with the GNU C Library; if not, see
9bb5d6
+   <https://www.gnu.org/licenses/>.  */
9bb5d6
+
9bb5d6
+#ifndef _TEST_SYSV_H
9bb5d6
+#define _TEST_SYSV_H
9bb5d6
+
9bb5d6
+#include <sys/ipc.h>
9bb5d6
+#include <sys/sem.h>
9bb5d6
+#include <sys/msg.h>
9bb5d6
+#include <sys/shm.h>
9bb5d6
+#include <include/array_length.h>
9bb5d6
+
9bb5d6
+/* Return the first invalid command SysV IPC command from common shared
9bb5d6
+   between message queue, shared memory, and semaphore.  */
9bb5d6
+static inline int
9bb5d6
+first_common_invalid_cmd (void)
9bb5d6
+{
9bb5d6
+  const int common_cmds[] = {
9bb5d6
+    IPC_RMID,
9bb5d6
+    IPC_SET,
9bb5d6
+    IPC_STAT,
9bb5d6
+    IPC_INFO,
9bb5d6
+  };
9bb5d6
+
9bb5d6
+  int invalid = 0;
9bb5d6
+  for (int i = 0; i < array_length (common_cmds); i++)
9bb5d6
+    {
9bb5d6
+      if (invalid == common_cmds[i])
9bb5d6
+	{
9bb5d6
+	  invalid++;
9bb5d6
+	  i = 0;
9bb5d6
+        }
9bb5d6
+    }
9bb5d6
+
9bb5d6
+  return invalid;
9bb5d6
+}
9bb5d6
+
9bb5d6
+/* Return the first invalid command SysV IPC command for semaphore.  */
9bb5d6
+static inline int
9bb5d6
+first_sem_invalid_cmd (void)
9bb5d6
+{
9bb5d6
+  const int sem_cmds[] = {
9bb5d6
+    GETPID,
9bb5d6
+    GETVAL,
9bb5d6
+    GETALL,
9bb5d6
+    GETNCNT,
9bb5d6
+    GETZCNT,
9bb5d6
+    SETVAL,
9bb5d6
+    SETALL,
9bb5d6
+    SEM_STAT,
9bb5d6
+    SEM_INFO,
9bb5d6
+#ifdef SEM_STAT_ANY
9bb5d6
+    SEM_STAT_ANY,
9bb5d6
+#endif
9bb5d6
+  };
9bb5d6
+
9bb5d6
+  int invalid = first_common_invalid_cmd ();
9bb5d6
+  for (int i = 0; i < array_length (sem_cmds); i++)
9bb5d6
+    {
9bb5d6
+      if (invalid == sem_cmds[i])
9bb5d6
+	{
9bb5d6
+	  invalid++;
9bb5d6
+	  i = 0;
9bb5d6
+	}
9bb5d6
+    }
9bb5d6
+
9bb5d6
+  return invalid;
9bb5d6
+}
9bb5d6
+
9bb5d6
+#endif /* _TEST_SYSV_H  */
9bb5d6
diff --git a/sysvipc/test-sysvsem.c b/sysvipc/test-sysvsem.c
9bb5d6
index d197772917a7579d..43a1460ec2b9308f 100644
9bb5d6
--- a/sysvipc/test-sysvsem.c
9bb5d6
+++ b/sysvipc/test-sysvsem.c
9bb5d6
@@ -25,6 +25,8 @@
9bb5d6
 #include <sys/ipc.h>
9bb5d6
 #include <sys/sem.h>
9bb5d6
 
9bb5d6
+#include <test-sysvipc.h>
9bb5d6
+
9bb5d6
 #include <support/support.h>
9bb5d6
 #include <support/check.h>
9bb5d6
 #include <support/temp_file.h>
9bb5d6
@@ -80,6 +82,9 @@ do_test (void)
9bb5d6
       FAIL_EXIT1 ("semget failed (errno=%d)", errno);
9bb5d6
     }
9bb5d6
 
9bb5d6
+  TEST_COMPARE (semctl (semid, 0, first_sem_invalid_cmd (), NULL), -1);
9bb5d6
+  TEST_COMPARE (errno, EINVAL);
9bb5d6
+
9bb5d6
   /* Get semaphore kernel information and do some sanity checks.  */
9bb5d6
   struct semid_ds seminfo;
9bb5d6
   if (semctl (semid, 0, IPC_STAT, (union semun) { .buf = &seminfo }) == -1)