public inbox for libc-stable@sourceware.org
 help / color / mirror / Atom feed
* [COMMITTED 1/3] sysvipc: Fix SEM_STAT_ANY kernel argument pass [BZ #26637]
@ 2020-10-15 13:59 Adhemerval Zanella
  2020-10-15 13:59 ` [COMMITTED 2/3] sysvipc: Fix IPC_INFO and MSG_INFO handling [BZ #26639] Adhemerval Zanella
  2020-10-15 13:59 ` [COMMITTED 3/3] sysvipc: Fix IPC_INFO and SHM_INFO handling [BZ #26636] Adhemerval Zanella
  0 siblings, 2 replies; 3+ messages in thread
From: Adhemerval Zanella @ 2020-10-15 13:59 UTC (permalink / raw)
  To: libc-stable

From: "Dmitry V. Levin" <ldv@altlinux.org>

Handle SEM_STAT_ANY the same way as SEM_STAT so that the buffer argument
of SEM_STAT_ANY is properly passed to the kernel and back.

The regression testcase checks for Linux specifix SysV ipc message
control extension.  For IPC_INFO/SEM_INFO it tries to match the values
against the tunable /proc values and for SEM_STAT/SEM_STAT_ANY it
check if the create message queue is within the global list returned
by the kernel.

Checked on x86_64-linux-gnu and on i686-linux-gnu (Linux v5.4 and on
Linux v4.15).

Co-authored-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

(cherry picked from commit 574500a108be1d2a6a0dc97a075c9e0a98371aba)
---
 NEWS                                        |   2 +
 sysdeps/unix/sysv/linux/Makefile            |   2 +-
 sysdeps/unix/sysv/linux/semctl.c            |   6 +
 sysdeps/unix/sysv/linux/tst-sysvsem-linux.c | 184 ++++++++++++++++++++
 sysvipc/test-sysvsem.c                      |   1 +
 5 files changed, 194 insertions(+), 1 deletion(-)
 create mode 100644 sysdeps/unix/sysv/linux/tst-sysvsem-linux.c

diff --git a/NEWS b/NEWS
index 3aad30baee..fb3a9e4f7d 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,8 @@ The following bugs are resolved with this release:
   [26534] libm.so 2.32 SIGILL in pow() due to FMA4 instruction on non-FMA4
     system
   [26555] string: strerrorname_np does not return the documented value
+  [26637] libc: semctl SEM_STAT_ANY fails to pass the buffer specified
+    by the caller to the kernel
 
 Version 2.32
 
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 9b2a253032..9b930e7466 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -100,7 +100,7 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
 	 tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \
 	 test-errno-linux tst-memfd_create tst-mlock2 tst-pkey \
 	 tst-rlimit-infinity tst-ofdlocks tst-gettid tst-gettid-kill \
-	 tst-tgkill
+	 tst-tgkill tst-sysvsem-linux
 tests-internal += tst-ofdlocks-compat tst-sigcontext-get_pc
 
 CFLAGS-tst-sigcontext-get_pc.c = -fasynchronous-unwind-tables
diff --git a/sysdeps/unix/sysv/linux/semctl.c b/sysdeps/unix/sysv/linux/semctl.c
index f131a26fc7..1cdabde8f2 100644
--- a/sysdeps/unix/sysv/linux/semctl.c
+++ b/sysdeps/unix/sysv/linux/semctl.c
@@ -102,6 +102,7 @@ semun64_to_ksemun64 (int cmd, union semun64 semun64,
       r.array = semun64.array;
       break;
     case SEM_STAT:
+    case SEM_STAT_ANY:
     case IPC_STAT:
     case IPC_SET:
       r.buf = buf;
@@ -150,6 +151,7 @@ __semctl64 (int semid, int semnum, int cmd, ...)
     case IPC_STAT:      /* arg.buf */
     case IPC_SET:
     case SEM_STAT:
+    case SEM_STAT_ANY:
     case IPC_INFO:      /* arg.__buf */
     case SEM_INFO:
       va_start (ap, cmd);
@@ -238,6 +240,7 @@ semun_to_semun64 (int cmd, union semun semun, struct __semid64_ds *semid64)
       r.array = semun.array;
       break;
     case SEM_STAT:
+    case SEM_STAT_ANY:
     case IPC_STAT:
     case IPC_SET:
       r.buf = semid64;
@@ -267,6 +270,7 @@ __semctl (int semid, int semnum, int cmd, ...)
     case IPC_STAT:      /* arg.buf */
     case IPC_SET:
     case SEM_STAT:
+    case SEM_STAT_ANY:
     case IPC_INFO:      /* arg.__buf */
     case SEM_INFO:
       va_start (ap, cmd);
@@ -321,6 +325,7 @@ __semctl_mode16 (int semid, int semnum, int cmd, ...)
     case IPC_STAT:      /* arg.buf */
     case IPC_SET:
     case SEM_STAT:
+    case SEM_STAT_ANY:
     case IPC_INFO:      /* arg.__buf */
     case SEM_INFO:
       va_start (ap, cmd);
@@ -354,6 +359,7 @@ __old_semctl (int semid, int semnum, int cmd, ...)
     case IPC_STAT:      /* arg.buf */
     case IPC_SET:
     case SEM_STAT:
+    case SEM_STAT_ANY:
     case IPC_INFO:      /* arg.__buf */
     case SEM_INFO:
       va_start (ap, cmd);
diff --git a/sysdeps/unix/sysv/linux/tst-sysvsem-linux.c b/sysdeps/unix/sysv/linux/tst-sysvsem-linux.c
new file mode 100644
index 0000000000..45f19e2d37
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-sysvsem-linux.c
@@ -0,0 +1,184 @@
+/* Basic tests for Linux SYSV semaphore extensions.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+
+/* These are for the temporary file we generate.  */
+static char *name;
+static int semid;
+
+static void
+remove_sem (void)
+{
+  /* Enforce message queue removal in case of early test failure.
+     Ignore error since the sem may already have being removed.  */
+  semctl (semid, 0, IPC_RMID, 0);
+}
+
+static void
+do_prepare (int argc, char *argv[])
+{
+  TEST_VERIFY_EXIT (create_temp_file ("tst-sysvsem.", &name) != -1);
+}
+
+#define PREPARE do_prepare
+
+#define SEM_MODE 0644
+
+union semun
+{
+  int val;
+  struct semid_ds *buf;
+  unsigned short  *array;
+  struct seminfo *__buf;
+};
+
+struct test_seminfo
+{
+  int semmsl;
+  int semmns;
+  int semopm;
+  int semmni;
+};
+
+/* It tries to obtain some system-wide SysV semaphore information from /proc
+   to check against IPC_INFO/SEM_INFO.  The /proc only returns the tunables
+   value of SEMMSL, SEMMNS, SEMOPM, and SEMMNI.
+
+   The kernel also returns constant value for SEMVMX, SEMMNU, SEMMAP, SEMUME,
+   and also SEMUSZ and SEMAEM (for IPC_INFO).  The issue to check them is they
+   might change over kernel releases.  */
+
+static void
+read_sem_stat (struct test_seminfo *tseminfo)
+{
+  FILE *f = fopen ("/proc/sys/kernel/sem", "r");
+  if (f == NULL)
+    FAIL_UNSUPPORTED ("/proc is not mounted or /proc/sys/kernel/sem is not "
+		      "available");
+
+  int r = fscanf (f, "%d %d %d %d",
+		  &tseminfo->semmsl, &tseminfo->semmns, &tseminfo->semopm,
+		  &tseminfo->semmni);
+  TEST_VERIFY_EXIT (r == 4);
+
+  fclose (f);
+}
+
+
+/* Check if the semaphore with IDX (index into the kernel's internal array)
+   matches the one with KEY.  The CMD is either SEM_STAT or SEM_STAT_ANY.  */
+
+static bool
+check_seminfo (int idx, key_t key, int cmd)
+{
+  struct semid_ds seminfo;
+  int sid = semctl (idx, 0, cmd, (union semun) { .buf = &seminfo });
+  /* Ignore unused array slot returned by the kernel or information from
+     unknown semaphores.  */
+  if ((sid == -1 && errno == EINVAL) || sid != semid)
+    return false;
+
+  if (sid == -1)
+    FAIL_EXIT1 ("semctl with SEM_STAT failed (errno=%d)", errno);
+
+  TEST_COMPARE (seminfo.sem_perm.__key, key);
+  TEST_COMPARE (seminfo.sem_perm.mode, SEM_MODE);
+  TEST_COMPARE (seminfo.sem_nsems, 1);
+
+  return true;
+}
+
+static int
+do_test (void)
+{
+  atexit (remove_sem);
+
+  key_t key = ftok (name, 'G');
+  if (key == -1)
+    FAIL_EXIT1 ("ftok failed: %m");
+
+  semid = semget (key, 1, IPC_CREAT | IPC_EXCL | SEM_MODE);
+  if (semid == -1)
+    FAIL_EXIT1 ("semget failed: %m");
+
+  struct test_seminfo tipcinfo;
+  read_sem_stat (&tipcinfo);
+
+  int semidx;
+
+  {
+    struct seminfo ipcinfo;
+    semidx = semctl (semid, 0, IPC_INFO, (union semun) { .__buf = &ipcinfo });
+    if (semidx == -1)
+      FAIL_EXIT1 ("semctl with IPC_INFO failed: %m");
+
+    TEST_COMPARE (ipcinfo.semmsl, tipcinfo.semmsl);
+    TEST_COMPARE (ipcinfo.semmns, tipcinfo.semmns);
+    TEST_COMPARE (ipcinfo.semopm, tipcinfo.semopm);
+    TEST_COMPARE (ipcinfo.semmni, tipcinfo.semmni);
+  }
+
+  /* Same as before but with SEM_INFO.  */
+  {
+    struct seminfo ipcinfo;
+    semidx = semctl (semid, 0, SEM_INFO, (union semun) { .__buf = &ipcinfo });
+    if (semidx == -1)
+      FAIL_EXIT1 ("semctl with IPC_INFO failed: %m");
+
+    TEST_COMPARE (ipcinfo.semmsl, tipcinfo.semmsl);
+    TEST_COMPARE (ipcinfo.semmns, tipcinfo.semmns);
+    TEST_COMPARE (ipcinfo.semopm, tipcinfo.semopm);
+    TEST_COMPARE (ipcinfo.semmni, tipcinfo.semmni);
+  }
+
+  /* We check if the created semaphore shows in the system-wide status.  */
+  bool found = false;
+  for (int i = 0; i <= semidx; i++)
+    {
+      /* We can't tell apart if SEM_STAT_ANY is not supported (kernel older
+	 than 4.17) or if the index used is invalid.  So it just check if
+	 value returned from a valid call matches the created semaphore.  */
+      check_seminfo (i, key, SEM_STAT_ANY);
+
+      if (check_seminfo (i, key, SEM_STAT))
+	{
+	  found = true;
+	  break;
+	}
+    }
+
+  if (!found)
+    FAIL_EXIT1 ("semctl with SEM_STAT/SEM_STAT_ANY could not find the "
+		"created  semaphore");
+
+  if (semctl (semid, 0, IPC_RMID, 0) == -1)
+    FAIL_EXIT1 ("semctl failed: %m");
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysvipc/test-sysvsem.c b/sysvipc/test-sysvsem.c
index 01dbff343a..b7284e0b48 100644
--- a/sysvipc/test-sysvsem.c
+++ b/sysvipc/test-sysvsem.c
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
+#include <stdbool.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/sem.h>
-- 
2.25.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [COMMITTED 2/3] sysvipc: Fix IPC_INFO and MSG_INFO handling [BZ #26639]
  2020-10-15 13:59 [COMMITTED 1/3] sysvipc: Fix SEM_STAT_ANY kernel argument pass [BZ #26637] Adhemerval Zanella
@ 2020-10-15 13:59 ` Adhemerval Zanella
  2020-10-15 13:59 ` [COMMITTED 3/3] sysvipc: Fix IPC_INFO and SHM_INFO handling [BZ #26636] Adhemerval Zanella
  1 sibling, 0 replies; 3+ messages in thread
From: Adhemerval Zanella @ 2020-10-15 13:59 UTC (permalink / raw)
  To: libc-stable

Both commands are Linux extensions where the third argument is a
'struct msginfo' instead of 'struct msqid_ds' and its information
does not contain any time related fields (so there is no need to
extra conversion for __IPC_TIME64.

The regression testcase checks for Linux specifix SysV ipc message
control extension.  For IPC_INFO/MSG_INFO it tries to match the values
against the tunable /proc values and for MSG_STAT/MSG_STAT_ANY it
check if the create message queue is within the global list returned
by the kernel.

Checked on x86_64-linux-gnu and on i686-linux-gnu (Linux v5.4 and on
Linux v4.15).

(cherry picked from commit 20a00dbefca5695cccaa44846a482db8ccdd85ab)
---
 NEWS                                        |   1 +
 sysdeps/unix/sysv/linux/Makefile            |   2 +-
 sysdeps/unix/sysv/linux/msgctl.c            |  22 ++-
 sysdeps/unix/sysv/linux/tst-sysvmsg-linux.c | 177 ++++++++++++++++++++
 4 files changed, 197 insertions(+), 5 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/tst-sysvmsg-linux.c

diff --git a/NEWS b/NEWS
index fb3a9e4f7d..15c0299dd0 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,7 @@ The following bugs are resolved with this release:
   [26555] string: strerrorname_np does not return the documented value
   [26637] libc: semctl SEM_STAT_ANY fails to pass the buffer specified
     by the caller to the kernel
+  [26639] libc: msgctl IPC_INFO and MSG_INFO return garbage
 
 Version 2.32
 
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 9b930e7466..6ea615ab00 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -100,7 +100,7 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
 	 tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \
 	 test-errno-linux tst-memfd_create tst-mlock2 tst-pkey \
 	 tst-rlimit-infinity tst-ofdlocks tst-gettid tst-gettid-kill \
-	 tst-tgkill tst-sysvsem-linux
+	 tst-tgkill tst-sysvsem-linux tst-sysvmsg-linux
 tests-internal += tst-ofdlocks-compat tst-sigcontext-get_pc
 
 CFLAGS-tst-sigcontext-get_pc.c = -fasynchronous-unwind-tables
diff --git a/sysdeps/unix/sysv/linux/msgctl.c b/sysdeps/unix/sysv/linux/msgctl.c
index 0776472d5e..a1f24ab242 100644
--- a/sysdeps/unix/sysv/linux/msgctl.c
+++ b/sysdeps/unix/sysv/linux/msgctl.c
@@ -90,8 +90,15 @@ __msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf)
   struct kernel_msqid64_ds ksemid, *arg = NULL;
   if (buf != NULL)
     {
-      msqid64_to_kmsqid64 (buf, &ksemid);
-      arg = &ksemid;
+      /* This is a Linux extension where kernel returns a 'struct msginfo'
+	 instead.  */
+      if (cmd == IPC_INFO || cmd == MSG_INFO)
+	arg = (struct kernel_msqid64_ds *) buf;
+      else
+	{
+	  msqid64_to_kmsqid64 (buf, &ksemid);
+	  arg = &ksemid;
+	}
     }
 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
   if (cmd == IPC_SET)
@@ -169,8 +176,15 @@ __msgctl (int msqid, int cmd, struct msqid_ds *buf)
   struct __msqid64_ds msqid64, *buf64 = NULL;
   if (buf != NULL)
     {
-      msqid_to_msqid64 (&msqid64, buf);
-      buf64 = &msqid64;
+      /* This is a Linux extension where kernel returns a 'struct msginfo'
+	 instead.  */
+      if (cmd == IPC_INFO || cmd == MSG_INFO)
+	buf64 = (struct __msqid64_ds *) buf;
+      else
+	{
+	  msqid_to_msqid64 (&msqid64, buf);
+	  buf64 = &msqid64;
+	}
     }
 
   int ret = __msgctl64 (msqid, cmd, buf64);
diff --git a/sysdeps/unix/sysv/linux/tst-sysvmsg-linux.c b/sysdeps/unix/sysv/linux/tst-sysvmsg-linux.c
new file mode 100644
index 0000000000..630f4f792c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-sysvmsg-linux.c
@@ -0,0 +1,177 @@
+/* Basic tests for Linux SYSV message queue extensions.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+
+#define MSGQ_MODE 0644
+
+/* These are for the temporary file we generate.  */
+static char *name;
+static int msqid;
+
+static void
+remove_msq (void)
+{
+  /* Enforce message queue removal in case of early test failure.
+     Ignore error since the msg may already have being removed.  */
+  msgctl (msqid, IPC_RMID, NULL);
+}
+
+static void
+do_prepare (int argc, char *argv[])
+{
+  TEST_VERIFY_EXIT (create_temp_file ("tst-sysvmsg.", &name) != -1);
+}
+
+#define PREPARE do_prepare
+
+struct test_msginfo
+{
+  int msgmax;
+  int msgmnb;
+  int msgmni;
+};
+
+/* It tries to obtain some system-wide SysV messsage queue information from
+   /proc to check against IPC_INFO/MSG_INFO.  The /proc only returns the
+   tunables value of MSGMAX, MSGMNB, and MSGMNI.
+
+   The kernel also returns constant value for MSGSSZ, MSGSEG and also MSGMAP,
+   MSGPOOL, and MSGTQL (for IPC_INFO).  The issue to check them is they might
+   change over kernel releases.  */
+
+static int
+read_proc_file (const char *file)
+{
+  FILE *f = fopen (file, "r");
+  if (f == NULL)
+    FAIL_UNSUPPORTED ("/proc is not mounted or %s is not available", file);
+
+  int v;
+  int r = fscanf (f, "%d", & v);
+  TEST_VERIFY_EXIT (r == 1);
+
+  fclose (f);
+  return v;
+}
+
+
+/* Check if the message queue with IDX (index into the kernel's internal
+   array) matches the one with KEY.  The CMD is either MSG_STAT or
+   MSG_STAT_ANY.  */
+
+static bool
+check_msginfo (int idx, key_t key, int cmd)
+{
+  struct msqid_ds msginfo;
+  int mid = msgctl (idx, cmd, &msginfo);
+  /* Ignore unused array slot returned by the kernel or information from
+     unknown message queue.  */
+  if ((mid == -1 && errno == EINVAL) || mid != msqid)
+    return false;
+
+  if (mid == -1)
+    FAIL_EXIT1 ("msgctl with %s failed: %m",
+		cmd == MSG_STAT ? "MSG_STAT" : "MSG_STAT_ANY");
+
+  TEST_COMPARE (msginfo.msg_perm.__key, key);
+  TEST_COMPARE (msginfo.msg_perm.mode, MSGQ_MODE);
+  TEST_COMPARE (msginfo.msg_qnum, 0);
+
+  return true;
+}
+
+static int
+do_test (void)
+{
+  atexit (remove_msq);
+
+  key_t key = ftok (name, 'G');
+  if (key == -1)
+    FAIL_EXIT1 ("ftok failed: %m");
+
+  msqid = msgget (key, MSGQ_MODE | IPC_CREAT);
+  if (msqid == -1)
+    FAIL_EXIT1 ("msgget failed: %m");
+
+  struct test_msginfo tipcinfo;
+  tipcinfo.msgmax = read_proc_file ("/proc/sys/kernel/msgmax");
+  tipcinfo.msgmnb = read_proc_file ("/proc/sys/kernel/msgmnb");
+  tipcinfo.msgmni = read_proc_file ("/proc/sys/kernel/msgmni");
+
+  int msqidx;
+
+  {
+    struct msginfo ipcinfo;
+    msqidx = msgctl (msqid, IPC_INFO, (struct msqid_ds *) &ipcinfo);
+    if (msqidx == -1)
+      FAIL_EXIT1 ("msgctl with IPC_INFO failed: %m");
+
+    TEST_COMPARE (ipcinfo.msgmax, tipcinfo.msgmax);
+    TEST_COMPARE (ipcinfo.msgmnb, tipcinfo.msgmnb);
+    TEST_COMPARE (ipcinfo.msgmni, tipcinfo.msgmni);
+  }
+
+  /* Same as before but with MSG_INFO.  */
+  {
+    struct msginfo ipcinfo;
+    msqidx = msgctl (msqid, MSG_INFO, (struct msqid_ds *) &ipcinfo);
+    if (msqidx == -1)
+      FAIL_EXIT1 ("msgctl with IPC_INFO failed: %m");
+
+    TEST_COMPARE (ipcinfo.msgmax, tipcinfo.msgmax);
+    TEST_COMPARE (ipcinfo.msgmnb, tipcinfo.msgmnb);
+    TEST_COMPARE (ipcinfo.msgmni, tipcinfo.msgmni);
+  }
+
+  /* We check if the created message queue shows in global list.  */
+  bool found = false;
+  for (int i = 0; i <= msqidx; i++)
+    {
+      /* We can't tell apart if MSG_STAT_ANY is not supported (kernel older
+	 than 4.17) or if the index used is invalid.  So it just check if the
+	 value returned from a valid call matches the created message
+	 queue.  */
+      check_msginfo (i, key, MSG_STAT_ANY);
+
+      if (check_msginfo (i, key, MSG_STAT))
+	{
+	  found = true;
+	  break;
+	}
+    }
+
+  if (!found)
+    FAIL_EXIT1 ("msgctl with MSG_STAT/MSG_STAT_ANY could not find the "
+		"created message queue");
+
+  if (msgctl (msqid, IPC_RMID, NULL) == -1)
+    FAIL_EXIT1 ("msgctl failed");
+
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.25.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [COMMITTED 3/3] sysvipc: Fix IPC_INFO and SHM_INFO handling [BZ #26636]
  2020-10-15 13:59 [COMMITTED 1/3] sysvipc: Fix SEM_STAT_ANY kernel argument pass [BZ #26637] Adhemerval Zanella
  2020-10-15 13:59 ` [COMMITTED 2/3] sysvipc: Fix IPC_INFO and MSG_INFO handling [BZ #26639] Adhemerval Zanella
@ 2020-10-15 13:59 ` Adhemerval Zanella
  1 sibling, 0 replies; 3+ messages in thread
From: Adhemerval Zanella @ 2020-10-15 13:59 UTC (permalink / raw)
  To: libc-stable

Both commands are Linux extensions where the third argument is either
a 'struct shminfo' (IPC_INFO) or a 'struct shm_info' (SHM_INFO) instead
of 'struct shmid_ds'.  And their information does not contain any time
related fields, so there is no need to extra conversion for __IPC_TIME64.

The regression testcase checks for Linux specifix SysV ipc message
control extension.  For SHM_INFO it tries to match the values against the
tunable /proc values and for MSG_STAT/MSG_STAT_ANY it check if the create\
shared memory is within the global list returned by the kernel.

Checked on x86_64-linux-gnu and on i686-linux-gnu (Linux v5.4 and on
Linux v4.15).

(cherry picked from commit  a49d7fd4f764e97ccaf922e433046590ae52fce9)
---
 NEWS                                        |   2 +
 sysdeps/unix/sysv/linux/Makefile            |   2 +-
 sysdeps/unix/sysv/linux/shmctl.c            |  24 ++-
 sysdeps/unix/sysv/linux/tst-sysvshm-linux.c | 185 ++++++++++++++++++++
 4 files changed, 206 insertions(+), 7 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/tst-sysvshm-linux.c

diff --git a/NEWS b/NEWS
index 15c0299dd0..3030735839 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,8 @@ The following bugs are resolved with this release:
   [26534] libm.so 2.32 SIGILL in pow() due to FMA4 instruction on non-FMA4
     system
   [26555] string: strerrorname_np does not return the documented value
+  [26636] libc: 32-bit shmctl(IPC_INFO) crashes when shminfo struct is
+    at the end of a memory mapping
   [26637] libc: semctl SEM_STAT_ANY fails to pass the buffer specified
     by the caller to the kernel
   [26639] libc: msgctl IPC_INFO and MSG_INFO return garbage
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 6ea615ab00..34748ffcd1 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -100,7 +100,7 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
 	 tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \
 	 test-errno-linux tst-memfd_create tst-mlock2 tst-pkey \
 	 tst-rlimit-infinity tst-ofdlocks tst-gettid tst-gettid-kill \
-	 tst-tgkill tst-sysvsem-linux tst-sysvmsg-linux
+	 tst-tgkill tst-sysvsem-linux tst-sysvmsg-linux tst-sysvshm-linux
 tests-internal += tst-ofdlocks-compat tst-sigcontext-get_pc
 
 CFLAGS-tst-sigcontext-get_pc.c = -fasynchronous-unwind-tables
diff --git a/sysdeps/unix/sysv/linux/shmctl.c b/sysdeps/unix/sysv/linux/shmctl.c
index 76d88441f1..1d19a798b1 100644
--- a/sysdeps/unix/sysv/linux/shmctl.c
+++ b/sysdeps/unix/sysv/linux/shmctl.c
@@ -90,8 +90,15 @@ __shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf)
   struct kernel_shmid64_ds kshmid, *arg = NULL;
   if (buf != NULL)
     {
-      shmid64_to_kshmid64 (buf, &kshmid);
-      arg = &kshmid;
+      /* This is a Linux extension where kernel expects either a
+	 'struct shminfo' (IPC_INFO) or 'struct shm_info' (SHM_INFO).  */
+      if (cmd == IPC_INFO || cmd == SHM_INFO)
+	arg = (struct kernel_shmid64_ds *) buf;
+      else
+	{
+	  shmid64_to_kshmid64 (buf, &kshmid);
+	  arg = &kshmid;
+	}
     }
 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
   if (cmd == IPC_SET)
@@ -107,7 +114,6 @@ __shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf)
 
   switch (cmd)
     {
-      case IPC_INFO:
       case IPC_STAT:
       case SHM_STAT:
       case SHM_STAT_ANY:
@@ -168,8 +174,15 @@ __shmctl (int shmid, int cmd, struct shmid_ds *buf)
   struct __shmid64_ds shmid64, *buf64 = NULL;
   if (buf != NULL)
     {
-      shmid_to_shmid64 (&shmid64, buf);
-      buf64 = &shmid64;
+      /* This is a Linux extension where kernel expects either a
+	 'struct shminfo' (IPC_INFO) or 'struct shm_info' (SHM_INFO).  */
+      if (cmd == IPC_INFO || cmd == SHM_INFO)
+	buf64 = (struct __shmid64_ds *) buf;
+      else
+	{
+	  shmid_to_shmid64 (&shmid64, buf);
+	  buf64 = &shmid64;
+	}
     }
 
   int ret = __shmctl64 (shmid, cmd, buf64);
@@ -178,7 +191,6 @@ __shmctl (int shmid, int cmd, struct shmid_ds *buf)
 
   switch (cmd)
     {
-      case IPC_INFO:
       case IPC_STAT:
       case SHM_STAT:
       case SHM_STAT_ANY:
diff --git a/sysdeps/unix/sysv/linux/tst-sysvshm-linux.c b/sysdeps/unix/sysv/linux/tst-sysvshm-linux.c
new file mode 100644
index 0000000000..7128ae2e14
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-sysvshm-linux.c
@@ -0,0 +1,185 @@
+/* Basic tests for Linux SYSV shared memory extensions.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+
+#define SHM_MODE 0644
+
+/* These are for the temporary file we generate.  */
+static char *name;
+static int shmid;
+static long int pgsz;
+
+static void
+remove_shm (void)
+{
+  /* Enforce message queue removal in case of early test failure.
+     Ignore error since the shm may already have being removed.  */
+  shmctl (shmid, IPC_RMID, NULL);
+}
+
+static void
+do_prepare (int argc, char *argv[])
+{
+  TEST_VERIFY_EXIT (create_temp_file ("tst-sysvshm.", &name) != -1);
+}
+
+#define PREPARE do_prepare
+
+struct test_shminfo
+{
+  unsigned long int shmall;
+  unsigned long int shmmax;
+  unsigned long int shmmni;
+};
+
+/* It tries to obtain some system-wide SysV shared memory information from
+   /proc to check against IPC_INFO/SHM_INFO.  The /proc only returns the
+   tunables value of SHMALL, SHMMAX, and SHMMNI.  */
+
+static uint64_t
+read_proc_file (const char *file)
+{
+  FILE *f = fopen (file, "r");
+  if (f == NULL)
+    FAIL_UNSUPPORTED ("/proc is not mounted or %s is not available", file);
+
+  /* Handle 32-bit binaries running on 64-bit kernels.  */
+  uint64_t v;
+  int r = fscanf (f, "%" SCNu64, &v);
+  TEST_VERIFY_EXIT (r == 1);
+
+  fclose (f);
+  return v;
+}
+
+
+/* Check if the message queue with IDX (index into the kernel's internal
+   array) matches the one with KEY.  The CMD is either SHM_STAT or
+   SHM_STAT_ANY.  */
+
+static bool
+check_shminfo (int idx, key_t key, int cmd)
+{
+  struct shmid_ds shminfo;
+  int sid = shmctl (idx, cmd, &shminfo);
+  /* Ignore unused array slot returned by the kernel or information from
+     unknown message queue.  */
+  if ((sid == -1 && errno == EINVAL) || sid != shmid)
+    return false;
+
+  if (sid == -1)
+    FAIL_EXIT1 ("shmctl with %s failed: %m",
+		cmd == SHM_STAT ? "SHM_STAT" : "SHM_STAT_ANY");
+
+  TEST_COMPARE (shminfo.shm_perm.__key, key);
+  TEST_COMPARE (shminfo.shm_perm.mode, SHM_MODE);
+  TEST_COMPARE (shminfo.shm_segsz, pgsz);
+
+  return true;
+}
+
+static int
+do_test (void)
+{
+  atexit (remove_shm);
+
+  pgsz = sysconf (_SC_PAGESIZE);
+  if (pgsz == -1)
+    FAIL_EXIT1 ("sysconf (_SC_PAGESIZE) failed: %m");
+
+  key_t key = ftok (name, 'G');
+  if (key == -1)
+    FAIL_EXIT1 ("ftok failed: %m");
+
+  shmid = shmget (key, pgsz, IPC_CREAT | IPC_EXCL | SHM_MODE);
+  if (shmid == -1)
+    FAIL_EXIT1 ("shmget failed: %m");
+
+  struct test_shminfo tipcinfo;
+  {
+    uint64_t v = read_proc_file ("/proc/sys/kernel/shmmax");
+#if LONG_MAX == INT_MAX
+    /* Kernel explicit clamp the value for shmmax on compat symbol (32-bit
+       binaries running on 64-bit kernels).  */
+    if (v > INT_MAX)
+      v = INT_MAX;
+#endif
+    tipcinfo.shmmax = v;
+  }
+  tipcinfo.shmall = read_proc_file ("/proc/sys/kernel/shmall");
+  tipcinfo.shmmni = read_proc_file ("/proc/sys/kernel/shmmni");
+
+  int shmidx;
+
+  /* Note: SHM_INFO does not return a shminfo, but rather a 'struct shm_info'.
+     It is tricky to verify its values since the syscall returns system wide
+     resources consumed by shared memory.  The shmctl implementation handles
+     SHM_INFO as IPC_INFO, so the IPC_INFO test should validate SHM_INFO as
+     well.  */
+
+  {
+    struct shminfo ipcinfo;
+    shmidx = shmctl (shmid, IPC_INFO, (struct shmid_ds *) &ipcinfo);
+    if (shmidx == -1)
+      FAIL_EXIT1 ("shmctl with IPC_INFO failed: %m");
+
+    TEST_COMPARE (ipcinfo.shmall, tipcinfo.shmall);
+    TEST_COMPARE (ipcinfo.shmmax, tipcinfo.shmmax);
+    TEST_COMPARE (ipcinfo.shmmni, tipcinfo.shmmni);
+  }
+
+  /* We check if the created shared memory shows in the global list.  */
+  bool found = false;
+  for (int i = 0; i <= shmidx; i++)
+    {
+      /* We can't tell apart if SHM_STAT_ANY is not supported (kernel older
+	 than 4.17) or if the index used is invalid.  So it just check if
+	 value returned from a valid call matches the created message
+	 queue.  */
+      check_shminfo (i, key, SHM_STAT_ANY);
+
+      if (check_shminfo (i, key, SHM_STAT))
+	{
+	  found = true;
+	  break;
+	}
+    }
+
+  if (!found)
+    FAIL_EXIT1 ("shmctl with SHM_STAT/SHM_STAT_ANY could not find the "
+		"created shared memory");
+
+  if (shmctl (shmid, IPC_RMID, NULL) == -1)
+    FAIL_EXIT1 ("shmctl failed");
+
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.25.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-10-15 14:00 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-15 13:59 [COMMITTED 1/3] sysvipc: Fix SEM_STAT_ANY kernel argument pass [BZ #26637] Adhemerval Zanella
2020-10-15 13:59 ` [COMMITTED 2/3] sysvipc: Fix IPC_INFO and MSG_INFO handling [BZ #26639] Adhemerval Zanella
2020-10-15 13:59 ` [COMMITTED 3/3] sysvipc: Fix IPC_INFO and SHM_INFO handling [BZ #26636] Adhemerval Zanella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).