lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <ZD7VkNWFfp22kTDt@datsun.rim.net>
Date:   Tue, 18 Apr 2023 14:38:24 -0300
From:   Brad Spencer <bspencer@...ckberry.com>
To:     netdev@...r.kernel.org
Cc:     linux-kernel@...r.kernel.org
Subject: netlink getsockopt() sets only one byte?

Calling getsockopt() on a netlink socket with SOL_NETLINK options that
use type int only sets the first byte of the int value but returns an
optlen equal to sizeof(int), at least on x86_64.


The detailed description:

It looks like netlink_getsockopt() calls put_user() with a char*
pointer, and I think that causes it to copy only one byte from the val
result, despite len being sizeof(int).

Is this the expected behaviour?  The returned size is 4, after all,
and other int-sized socket options (outside of netlink) like
SO_REUSEADDR set all bytes of the int.

Programs that do not expect this behaviour and do not initialize the
value to some known bit pattern are likely to misinterpret the result,
especially when checking to see if the value is or isn't zero.

Attached is a short program that demonstrates the issue on Arch Linux
with the 6.3.0-rc6 mainline kernel on x86_64, and also with the same
Arch Linux userland on 6.2.10-arch1-1.  I've seen the same behaviour
on older Debian and Ubuntu kernels.

    gcc -Wall -o prog prog.c
    
Show only the first byte being written to when the setting is `0`:

    $ ./progboot
    SOL_SOCKET SO_REUSEADDR:
    size=4 value=0x0
    SOL_NETLINK NETLINK_NO_ENOBUFS:
    size=4 value=0xdeadbe00
    prog: prog.c:39: tryOption: Assertion `value == 0' failed.
    Aborted (core dumped)

Workaround by initializing to zero:

    $ ./prog workaround
    SOL_SOCKET SO_REUSEADDR:
    size=4 value=0x0
    SOL_NETLINK NETLINK_NO_ENOBUFS:
    size=4 value=0x0

Show only the first byte being written to when the setting is `1`:

    $ SET_FIRST=yes ./prog
    SOL_SOCKET SO_REUSEADDR:
    size=4 value=0x1
    SOL_NETLINK NETLINK_NO_ENOBUFS:
    size=4 value=0xdeadbe01
    prog: prog.c:35: tryOption: Assertion `value == 1' failed.
    Aborted (core dumped)

Workaround by initializing to zero:

    $ SET_FIRST=yes ./prog workaround
    SOL_SOCKET SO_REUSEADDR:
    size=4 value=0x1
    SOL_NETLINK NETLINK_NO_ENOBUFS:
    size=4 value=0x1


Demonstration program:

#include <asm/types.h>
#include <assert.h>
#include <linux/netlink.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <unistd.h>

static void tryOption(const int fd,
                      const int level,
                      const int optname,
                      const int workaround,
                      const int setFirst)
{
  assert(fd != -1);
  
  // Setting it to 1 gives similar results.
  if(setFirst)
  {
    int value = 1;
    assert(!setsockopt(fd, level, optname, &value, sizeof(value)));
  }

  // Get the option.
  {
    int value = workaround ? 0 : 0xdeadbeef;
    socklen_t size = sizeof(value);

    // Only the first byte of `value` is written to!
    assert(!getsockopt(fd, level, optname, &value, &size));
    printf("size=%u value=0x%x\n", size, value);
    if(setFirst)
    {
      assert(value == 1);
    }
    else
    {
      assert(value == 0);
    }

    // But it always reports a 4 byte option size.
    assert(size == sizeof(int));
  }

  close(fd);
}

int
main(int argc, char** argv)
{
  // If any argument is supplied, apply a workaround.
  const int workaround = argc > 1;

  // If $SET_FIRST is set to anything, set the option to 1 first.
  const int setFirst = getenv("SET_FIRST") != NULL;

  // Other int options set all bytes of the int.
  printf("SOL_SOCKET SO_REUSEADDR:\n");
  tryOption(
    socket(AF_INET, SOCK_STREAM, 0),
    SOL_SOCKET,
    SO_REUSEADDR,
    workaround,
    setFirst);

  // Netlink int socket options do not set all bytes of the int.
  printf("SOL_NETLINK NETLINK_NO_ENOBUFS:\n");
  tryOption(
    socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE),
    SOL_NETLINK,
    NETLINK_NO_ENOBUFS,
    workaround,
    setFirst);
}

-- 
Brad Spencer

----------------------------------------------------------------------
This transmission (including any attachments) may contain confidential information, privileged material (including material protected by the solicitor-client or other applicable privileges), or constitute non-public information. Any use of this information by anyone other than the intended recipient is prohibited. If you have received this transmission in error, please immediately reply to the sender and delete this information from your system. Use, dissemination, distribution, or reproduction of this transmission by unintended recipients is not authorized and may be unlawful.

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ