1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-04-26 15:09:05 +03:00
Adhemerval Zanella 24fdebe75f linux: Clear mode_t padding bits (BZ#25623)
The kernel might not clear the padding value for the ipc_perm mode
fields in compat mode (32 bit running on a 64 bit kernel).  It was
fixed on v4.14 when the ipc compat code was refactored to move
(commits 553f770ef71b, 469391684626, c0ebccb6fa1e).

Although it is most likely a kernel issue, it was shown only due
BZ#18231 fix which made all the SysVIPC mode_t 32-bit regardless of
the kABI.

This patch fixes it by explicitly zeroing the upper bits for such
cases.  The __ASSUME_SYSVIPC_BROKEN_MODE_T case already handles
it with the shift.

(The aarch64 ipc_priv.h is superflous since
__ASSUME_SYSVIPC_DEFAULT_IPC_64 is now defined as default).

Checked on i686-linux-gnu on 3.10 and on 4.15 kernel.
2020-03-05 14:40:28 -03:00

131 lines
4.0 KiB
C

/* Copyright (C) 1995-2020 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, see
<https://www.gnu.org/licenses/>. */
#include <sys/msg.h>
#include <ipc_priv.h>
#include <sysdep.h>
#include <shlib-compat.h>
#include <errno.h>
#ifndef DEFAULT_VERSION
# ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
# define DEFAULT_VERSION GLIBC_2_2
# else
# define DEFAULT_VERSION GLIBC_2_31
# endif
#endif
static int
msgctl_syscall (int msqid, int cmd, struct msqid_ds *buf)
{
#ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
return INLINE_SYSCALL_CALL (msgctl, msqid, cmd | __IPC_64, buf);
#else
return INLINE_SYSCALL_CALL (ipc, IPCOP_msgctl, msqid, cmd | __IPC_64, 0,
buf);
#endif
}
int
__new_msgctl (int msqid, int cmd, struct msqid_ds *buf)
{
/* POSIX states ipc_perm mode should have type of mode_t. */
_Static_assert (sizeof ((struct msqid_ds){0}.msg_perm.mode)
== sizeof (mode_t),
"sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
struct msqid_ds tmpds;
if (cmd == IPC_SET)
{
tmpds = *buf;
tmpds.msg_perm.mode *= 0x10000U;
buf = &tmpds;
}
#endif
int ret = msgctl_syscall (msqid, cmd, buf);
if (ret >= 0)
{
switch (cmd)
{
case IPC_STAT:
case MSG_STAT:
case MSG_STAT_ANY:
#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
buf->msg_perm.mode >>= 16;
#else
/* Old Linux kernel versions might not clear the mode padding. */
if (sizeof ((struct msqid_ds){0}.msg_perm.mode)
!= sizeof (__kernel_mode_t))
buf->msg_perm.mode &= 0xFFFF;
#endif
}
}
return ret;
}
versioned_symbol (libc, __new_msgctl, msgctl, DEFAULT_VERSION);
#if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
&& SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
int
attribute_compat_text_section
__msgctl_mode16 (int msqid, int cmd, struct msqid_ds *buf)
{
return msgctl_syscall (msqid, cmd, buf);
}
compat_symbol (libc, __msgctl_mode16, msgctl, GLIBC_2_2);
#endif
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
struct __old_msqid_ds
{
struct __old_ipc_perm msg_perm; /* structure describing operation permission */
struct msg *__msg_first; /* pointer to first message on queue */
struct msg *__msg_last; /* pointer to last message on queue */
__time_t msg_stime; /* time of last msgsnd command */
__time_t msg_rtime; /* time of last msgrcv command */
__time_t msg_ctime; /* time of last change */
struct wait_queue *__wwait; /* ??? */
struct wait_queue *__rwait; /* ??? */
unsigned short int __msg_cbytes; /* current number of bytes on queue */
unsigned short int msg_qnum; /* number of messages currently on queue */
unsigned short int msg_qbytes; /* max number of bytes allowed on queue */
__ipc_pid_t msg_lspid; /* pid of last msgsnd() */
__ipc_pid_t msg_lrpid; /* pid of last msgrcv() */
};
int
attribute_compat_text_section
__old_msgctl (int msqid, int cmd, struct __old_msqid_ds *buf)
{
#if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
&& !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
/* For architecture that have wire-up msgctl but also have __IPC_64 to a
value different than default (0x0) it means the compat symbol used the
__NR_ipc syscall. */
return INLINE_SYSCALL_CALL (msgctl, msqid, cmd, buf);
#else
return INLINE_SYSCALL_CALL (ipc, IPCOP_msgctl, msqid, cmd, 0, buf);
#endif
}
compat_symbol (libc, __old_msgctl, msgctl, GLIBC_2_0);
#endif