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: <CALCETrWUtYmSWw9-K1To8UDHe5THqEiwVyeSRNFQBaGuHs4cgg@mail.gmail.com>
Date: Thu, 15 Feb 2024 13:17:30 -0800
From: Andy Lutomirski <luto@...capital.net>
To: Wei Wang <weiwan@...gle.com>, Network Development <netdev@...r.kernel.org>, 
	Eric Dumazet <edumazet@...gle.com>, "David S. Miller" <davem@...emloft.net>
Subject: SO_RESERVE_MEM doesn't quite work, at least on UDP

With SO_RESERVE_MEM, I can reserve memory, and it gets credited to
sk_forward_alloc.  But, as far as I can tell, nothing keeps it from
getting "reclaimed" (i.e. un-credited from sk_forward_alloc and
uncharged).  So, for UDP at least, it basically doesn't work.

Here's a test program in Python:

#!/usr/bin/python3

import typing
import argparse
import socket
import sys
import dataclasses
import struct

SO_RESERVE_MEM = 73

@dataclasses.dataclass
class MemInfo:
    RMEM_ALLOC : int
    RCVBUF : int
    WMEM_ALLOC : int
    SNDBUF : int
    FWD_ALLOC : int
    WMEM_QUEUED : int
    OPTMEM : int
    BACKLOG : int
    DROPS : int

def sk_meminfo(s : socket.socket) -> MemInfo:
    nvars = len(dataclasses.fields(MemInfo))
    buf = s.getsockopt(socket.SOL_SOCKET, 55, 4*nvars) # SO_MEMINFO
    return MemInfo(*struct.unpack(f"{nvars}I", buf))

def main() -> None:
    parser = argparse.ArgumentParser(description='Measure UDP socket
receive buffers')
    parser.add_argument('--sendsize', type=int, default=1024,
                        help='Size of each individual send')
    parser.add_argument('--rcvbuf', type=int,
                        help='SO_RCVBUF')
    parser.add_argument('--reserve-mem', type=int,
                        help='SO_RESERVE_MEM')
    args = parser.parse_args()

    receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
    receiver.bind(('127.0.0.1', 0))
    bound_addr = receiver.getsockname()
    print('Receiving on %s:%d' % bound_addr, file=sys.stderr)

    sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
    sender.connect(bound_addr)

    if args.rcvbuf is not None:
        receiver.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, args.rcvbuf)
    print('SO_RCVBUF = %d' % receiver.getsockopt(socket.SOL_SOCKET,
socket.SO_RCVBUF))

    if args.reserve_mem is not None:
        receiver.setsockopt(socket.SOL_SOCKET, SO_RESERVE_MEM, args.reserve_mem)
        print('SO_RESERVE_MEM %d succeeded' % args.reserve_mem)

    print(f"{'sent':>10} {'total':>10}")

    meminfo = sk_meminfo(receiver)
    print(f"{'':>10} {0:>10} {meminfo}")

    bytes_sent = 0
    while meminfo.DROPS == 0:
        bytes_now = args.sendsize
        sender.send(bytes_now * b'x')
        bytes_sent += bytes_now

        meminfo = sk_meminfo(receiver)
        print(f"{bytes_now:>10} {bytes_sent:>10} {meminfo}")

    # Be polite to anyone watching using dropwatch or similar tools:
drain the socket
    receiver.setblocking(False)
    ndgrams = 0
    while True:
        try:
            receiver.recv(1)
            ndgrams += 1
        except OSError:
            break
    print('Drained %d datagrams' % ndgrams)

    meminfo = sk_meminfo(receiver)
    print(f"{'':>10} {'':>10} {meminfo}")


if __name__ == '__main__':
    main()

SO_RCVBUF = 212992
SO_RESERVE_MEM 100000 succeeded
      sent      total
                    0 MemInfo(RMEM_ALLOC=0, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=102400, WMEM_QUEUED=0,
OPTMEM=0, BACKLOG=0, DROPS=0)
      1024       1024 MemInfo(RMEM_ALLOC=2304, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=100096, WMEM_QUEUED=0,
OPTMEM=0, BACKLOG=0, DROPS=0)
      1024       2048 MemInfo(RMEM_ALLOC=4608, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=97792, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024       3072 MemInfo(RMEM_ALLOC=6912, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=95488, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024       4096 MemInfo(RMEM_ALLOC=9216, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=93184, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024       5120 MemInfo(RMEM_ALLOC=11520, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=90880, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024       6144 MemInfo(RMEM_ALLOC=13824, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=88576, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024       7168 MemInfo(RMEM_ALLOC=16128, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=86272, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024       8192 MemInfo(RMEM_ALLOC=18432, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=83968, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024       9216 MemInfo(RMEM_ALLOC=20736, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=81664, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024      10240 MemInfo(RMEM_ALLOC=23040, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=79360, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024      11264 MemInfo(RMEM_ALLOC=25344, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=77056, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024      12288 MemInfo(RMEM_ALLOC=27648, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=74752, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024      13312 MemInfo(RMEM_ALLOC=29952, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=72448, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024      14336 MemInfo(RMEM_ALLOC=32256, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=70144, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)

<-- Okay, looks like it's working as expected.

      1024      94208 MemInfo(RMEM_ALLOC=211968, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=1024, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
      1024      95232 MemInfo(RMEM_ALLOC=214272, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=2816, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)

<-- And now we're out of space.

      1024      96256 MemInfo(RMEM_ALLOC=214272, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=2816, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=1)

<-- And we drop the next datagram, as expected

Drained 93 datagrams
                      MemInfo(RMEM_ALLOC=0, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=4096, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=1)

<-- Now read all the queued data.  Whoops, sk_forward_alloc == 4096.
Where'd the reservation go?

Am I right that this is a bug?  What's the right way to fix it?

--Andy

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ