summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYDBot <[email protected]>2026-07-16 01:03:32 +0000
committerYDBot <[email protected]>2026-07-16 01:03:32 +0000
commit4fd409e7518f252e69af6a97c9985ac75217bc05 (patch)
treedfeb8933f25774369a38a16c0347369bca7e2a04
parent73c8481330d15583a0f3f94aa7e496077a700d53 (diff)
parent391306e72edb239eb7ac945e2d2c72a40564ff02 (diff)
Sync branches 260716-0101
-rw-r--r--build/mapping.conf.json4
-rw-r--r--build/platform/test_tool/host.ya.make.inc10
-rw-r--r--build/platform/test_tool/host_os.ya.make.inc10
-rw-r--r--build/plugins/_dart_fields.py7
-rw-r--r--build/plugins/nots.py2
-rw-r--r--build/ya.conf.json106
-rwxr-xr-xbuild/ymake_conf.py36
-rw-r--r--contrib/libs/croaring/.yandex_meta/override.nix4
-rw-r--r--contrib/libs/croaring/AGENTS.md8
-rw-r--r--contrib/libs/croaring/cpp/roaring/roaring.hh8
-rw-r--r--contrib/libs/croaring/include/roaring/roaring_version.h4
-rw-r--r--contrib/libs/croaring/src/bitset_util.c4
-rw-r--r--contrib/libs/croaring/ya.make4
-rw-r--r--contrib/libs/liburing/.yandex_meta/devtools.licenses.report43
-rw-r--r--contrib/libs/liburing/.yandex_meta/override.nix4
-rw-r--r--contrib/libs/liburing/README8
-rw-r--r--contrib/libs/liburing/src/arch/aarch64/lib.h13
-rw-r--r--contrib/libs/liburing/src/arch/syscall-defs.h4
-rw-r--r--contrib/libs/liburing/src/include/liburing.h76
-rw-r--r--contrib/libs/liburing/src/include/liburing/io_uring.h72
-rw-r--r--contrib/libs/liburing/src/include/liburing/io_uring/bpf_filter.h68
-rw-r--r--contrib/libs/liburing/src/include/liburing/io_uring/query.h28
-rw-r--r--contrib/libs/liburing/src/include/liburing/io_uring_version.h2
-rw-r--r--contrib/libs/liburing/src/nolibc.c9
-rw-r--r--contrib/libs/liburing/src/queue.c68
-rw-r--r--contrib/libs/liburing/src/register.c44
-rw-r--r--contrib/libs/liburing/src/setup.c31
-rw-r--r--contrib/libs/liburing/test/232c93d07b74.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/35fa71a030ca.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/500f9fbadef8.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/7ad0e4b2f83c.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/8a9973408177.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/917257daa0fe.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/a0908ae19763.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/a4c0b3decb33.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/accept-link.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/accept-mshot-stress.c514
-rw-r--r--contrib/libs/liburing/test/accept-mshot-stress.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/accept-non-empty.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/accept-reuse.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/accept-test.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/accept.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/across-fork.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/b19062a56726.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/b5837bd5311d.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/bind-listen.c9
-rw-r--r--contrib/libs/liburing/test/bind-listen.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/buf-ring-mshot.c452
-rw-r--r--contrib/libs/liburing/test/buf-ring-mshot.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/buf-ring-nommap.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/buf-ring-put.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/buf-ring-stress.c495
-rw-r--r--contrib/libs/liburing/test/buf-ring-stress.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/buf-ring-upgrade.c255
-rw-r--r--contrib/libs/liburing/test/buf-ring-upgrade.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/buf-ring.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/cancel-fd-userdata.c360
-rw-r--r--contrib/libs/liburing/test/cancel-fd-userdata.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/cancel-race.c437
-rw-r--r--contrib/libs/liburing/test/cancel-race.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/cbpf_filter.c2776
-rw-r--r--contrib/libs/liburing/test/cbpf_filter.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/ce593a6c480a.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/close-opath.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/cmd-discard.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/conn-unreach.c2
-rw-r--r--contrib/libs/liburing/test/conn-unreach.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/connect-rep.c6
-rw-r--r--contrib/libs/liburing/test/connect-rep.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/connect.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/coredump.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/cq-full.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/cq-overflow.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/cq-peek-batch-mixed.c190
-rw-r--r--contrib/libs/liburing/test/cq-peek-batch-mixed.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/cq-peek-batch.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/cq-ready.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/cq-size.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/d4ae271dfaae.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/d77a67ed5f27.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/defer-taskrun.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/defer-tw-timeout.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/defer.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/double-poll-crash.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/drop-submit.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/eeed8b54e0df.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/empty-eownerdead.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/eploop.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/epwait.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/eventfd-disable.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/eventfd-reg.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/eventfd-ring.c6
-rw-r--r--contrib/libs/liburing/test/eventfd-ring.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/eventfd.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/evfd-short-read.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/evloop.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/exec-target.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/exit-no-cleanup.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fadvise.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fallocate.c2
-rw-r--r--contrib/libs/liburing/test/fallocate.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fc2a85cb02ef.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fd-install.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fd-pass.c4
-rw-r--r--contrib/libs/liburing/test/fd-pass.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fdinfo-sqpoll.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fdinfo.c20
-rw-r--r--contrib/libs/liburing/test/fdinfo.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fifo-futex-poll.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fifo-nonblock-read.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/file-alloc-range-hint.c219
-rw-r--r--contrib/libs/liburing/test/file-alloc-range-hint.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/file-exit-unreg.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/file-register.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/file-update.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/file-verify.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/files-exit-hang-poll.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/files-exit-hang-timeout.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fixed-buf-iter.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fixed-buf-merge.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fixed-hugepage.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fixed-link.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fixed-reuse.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fixed-seg.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fpos.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fsnotify.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/fsync.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/futex-kill.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/futex.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/hardlink.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/ignore-single-mmap.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/init-mem.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/io-cancel.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/io-wq-exit.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/io-wq-unused-exit.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/io_uring_enter.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/io_uring_passthrough.c14
-rw-r--r--contrib/libs/liburing/test/io_uring_passthrough.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/io_uring_register.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/io_uring_setup.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/iopoll-leak.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/iopoll-overflow.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/iopoll-sync.c128
-rw-r--r--contrib/libs/liburing/test/iopoll-sync.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/iopoll.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/iowait.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/kallsyms.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/large-resize.c474
-rw-r--r--contrib/libs/liburing/test/large-resize.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/lfs-openat-write.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/lfs-openat.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/link-timeout.c2
-rw-r--r--contrib/libs/liburing/test/link-timeout.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/link.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/link_drain.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/linked-defer-close.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/madvise.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/min-timeout-wait.c2
-rw-r--r--contrib/libs/liburing/test/min-timeout-wait.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/min-timeout.c2
-rw-r--r--contrib/libs/liburing/test/min-timeout.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/mkdir.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/mock_file.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/msg-ring-fd.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/msg-ring-flags.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/msg-ring-overflow.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/msg-ring.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/mshot-shutdown-race.c227
-rw-r--r--contrib/libs/liburing/test/mshot-shutdown-race.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/multicqes_drain.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/napi-test.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/no-mmap-inval.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/nolibc.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/nop-all-sizes.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/nop-fixed-file-leak.c101
-rw-r--r--contrib/libs/liburing/test/nop-fixed-file-leak.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/nop-flags.c745
-rw-r--r--contrib/libs/liburing/test/nop-flags.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/nop.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/nop32-overflow.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/nop32.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/ooo-file-unreg.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/open-close.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/open-direct-link.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/open-direct-pick.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/openat2.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/personality.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/pipe-bug.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/pipe-direct-fixed.c243
-rw-r--r--contrib/libs/liburing/test/pipe-direct-fixed.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/pipe-eof.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/pipe-reuse.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/pipe.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-cancel-all.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-cancel-ton.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-cancel.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-link.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-many.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-mshot-overflow.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-mshot-update.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-mshot-wake.c127
-rw-r--r--contrib/libs/liburing/test/poll-mshot-wake.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/poll-race-mshot.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-race.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-ring.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-update-trigger.c4
-rw-r--r--contrib/libs/liburing/test/poll-update-trigger.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll-v-poll.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/poll.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/pollfree.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/probe.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/read-before-exit.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/read-inc-buf-more.c280
-rw-r--r--contrib/libs/liburing/test/read-inc-buf-more.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/read-inc-file.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/read-mshot-empty.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/read-mshot-stdin.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/read-mshot.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/read-write.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/recv-bundle-buf-len.c349
-rw-r--r--contrib/libs/liburing/test/recv-bundle-buf-len.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/recv-bundle-short-ooo.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/recv-inc-ooo.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/recv-msgall-stream.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/recv-msgall.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/recv-mshot-drain.c318
-rw-r--r--contrib/libs/liburing/test/recv-mshot-drain.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/recv-mshot-fair.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/recv-multishot.c65
-rw-r--r--contrib/libs/liburing/test/recv-multishot.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/recvmsg-inc-tail.c319
-rw-r--r--contrib/libs/liburing/test/recvmsg-inc-tail.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/recvsend_bundle-inc.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/recvsend_bundle.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/reg-fd-only.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/reg-hint.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/reg-reg-ring.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/reg-wait.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/regbuf-clone.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/regbuf-merge.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/register-restrictions.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/rename.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/resize-mmap-fail.c125
-rw-r--r--contrib/libs/liburing/test/resize-mmap-fail.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/resize-rings.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/ring-leak.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/ring-leak2.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/ring-query.c15
-rw-r--r--contrib/libs/liburing/test/ring-query.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/ringbuf-loop.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/ringbuf-read.c2
-rw-r--r--contrib/libs/liburing/test/ringbuf-read.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/ringbuf-status.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/rsrc_tags.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/rw_merge_test.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/self.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/send-zerocopy.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/send_recv.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/send_recvmsg.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sendmsg_iov_clean.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sendzc-bug.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/shared-wq.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/short-read.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/shutdown.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sigfd-deadlock.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/single-issuer.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/skip-cqe.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/socket-getsetsock-cmd.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/socket-io-cmd.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/socket-nb.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/socket-rw-eagain.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/socket-rw-offset.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/socket-rw.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/socket.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/splice.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sq-full-cpp.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sq-full.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sq-poll-dup.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sq-poll-kthread.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sq-poll-share.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sq-space_left.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sqe-mixed-bad-wrap.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sqe-mixed-boundary.c185
-rw-r--r--contrib/libs/liburing/test/sqe-mixed-boundary.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/sqe-mixed-nop.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sqe-mixed-uring_cmd.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sqpoll-disable-exit.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sqpoll-exec.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sqpoll-exit-hang.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sqpoll-sleep.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sqwait.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/stdout.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/submit-and-wait.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/submit-link-fail.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/submit-reuse.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/symlink.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/sync-cancel.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/task-restrict.c415
-rw-r--r--contrib/libs/liburing/test/task-restrict.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/teardowns.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/thread-exit.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/timens-abs-timer.c316
-rw-r--r--contrib/libs/liburing/test/timens-abs-timer.t/ya.make35
-rw-r--r--contrib/libs/liburing/test/timeout-new.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/timeout.c61
-rw-r--r--contrib/libs/liburing/test/timeout.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/timerfd-short-read.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/timestamp-bug.c5
-rw-r--r--contrib/libs/liburing/test/timestamp-bug.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/timestamp.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/truncate.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/tty-write-dpoll.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/unlink.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/vec-regbuf.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/version.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/wait-timeout.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/waitid.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/wakeup-hang.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/wq-aff.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/xattr.t/ya.make2
-rw-r--r--contrib/libs/liburing/test/zcrx.c1660
-rw-r--r--contrib/libs/liburing/test/zcrx.t/ya.make2
-rw-r--r--contrib/libs/liburing/ya.make28
-rw-r--r--contrib/libs/openssl/.yandex_meta/__init__.py13
-rw-r--r--contrib/libs/openssl/apps/progs-linux.h507
-rw-r--r--contrib/libs/openssl/apps/progs-win.h507
-rw-r--r--contrib/libs/openssl/apps/progs.h510
-rw-r--r--contrib/libs/openssl/patches/quictls-from-ms.patch1657
-rwxr-xr-xya20
-rw-r--r--yql/essentials/cfg/tests/gateways-experimental.conf5
-rw-r--r--yql/essentials/core/common_opt/yql_co_flow2.cpp4
-rw-r--r--yql/essentials/core/common_opt/yql_co_transformer.cpp3
-rw-r--r--yql/essentials/core/facade/yql_facade.cpp13
-rw-r--r--yql/essentials/core/facade/yql_facade.h3
-rw-r--r--yql/essentials/core/langver/feature.cpp12
-rw-r--r--yql/essentials/core/langver/feature.gen.py4
-rw-r--r--yql/essentials/core/langver/feature.h2
-rw-r--r--yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp20
-rw-r--r--yql/essentials/core/type_ann/type_ann_core.cpp43
-rw-r--r--yql/essentials/core/type_ann/type_ann_impl.h2
-rw-r--r--yql/essentials/core/type_ann/type_ann_join.cpp4
-rw-r--r--yql/essentials/core/type_ann/type_ann_list.cpp18
-rw-r--r--yql/essentials/core/type_ann/type_ann_list.h6
-rw-r--r--yql/essentials/core/type_ann/type_ann_types.cpp5
-rw-r--r--yql/essentials/core/ut/ya.make1
-rw-r--r--yql/essentials/core/ut/yql_expr_type_annotation_ut.cpp170
-rw-r--r--yql/essentials/core/yql_execution.cpp3
-rw-r--r--yql/essentials/core/yql_expr_type_annotation.cpp105
-rw-r--r--yql/essentials/core/yql_expr_type_annotation.h8
-rw-r--r--yql/essentials/core/yql_join.cpp5
-rw-r--r--yql/essentials/core/yql_join.h3
-rw-r--r--yql/essentials/core/yql_sql_combine_expander.cpp18
-rw-r--r--yql/essentials/core/yql_type_annotation.h6
-rw-r--r--yql/essentials/core/yql_window_features.cpp4
-rw-r--r--yql/essentials/data/language/features.json92
-rw-r--r--yql/essentials/parser/pg_wrapper/ubsan.supp1
-rw-r--r--yql/essentials/providers/config/yql_config_provider.cpp11
-rw-r--r--yql/essentials/sql/pg/pg_sql.cpp3
-rw-r--r--yql/essentials/sql/pg/pg_sql_ut.cpp28
-rw-r--r--yql/essentials/sql/pg/ya.make1
-rw-r--r--yql/essentials/sql/v1/builtin.cpp75
-rw-r--r--yql/essentials/sql/v1/sql_ut_common.h2
-rw-r--r--yql/essentials/tests/sql/minirun/part12/canondata/result.json4
-rw-r--r--yql/essentials/tests/sql/minirun/part13/canondata/result.json14
-rw-r--r--yql/essentials/tests/sql/minirun/part15/canondata/result.json2
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/result.json26
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-decimal_common_type_conversions_with_fix_/formatted.sql8
-rw-r--r--yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-min_of_decimal_fail_/formatted.sql6
-rw-r--r--yql/essentials/tests/sql/suites/expr/decimal_common_type_conversions_with_fix.yql7
-rw-r--r--yql/essentials/tests/sql/suites/expr/min_of_decimal_fail.cfg1
-rw-r--r--yql/essentials/tests/sql/suites/expr/min_of_decimal_fail.yql4
-rw-r--r--yql/essentials/tools/yql_facade_run/yql_facade_run.cpp5
-rw-r--r--yql/essentials/tools/yql_facade_run/yql_facade_run.h1
-rw-r--r--yql/essentials/udfs/common/datetime2/datetime_udf.cpp9
-rw-r--r--yql/essentials/udfs/common/datetime2/ya.make1
-rw-r--r--yql/essentials/udfs/common/digest/digest_udf.cpp3
-rw-r--r--yql/essentials/udfs/common/digest/ya.make1
-rw-r--r--yql/essentials/udfs/common/ip_base/lib/ip_base_udf.h5
-rw-r--r--yql/essentials/udfs/common/ip_base/lib/ya.make1
-rw-r--r--yql/essentials/udfs/common/math/math_udf.cpp3
-rw-r--r--yql/essentials/udfs/common/math/ya.make1
-rw-r--r--yql/essentials/udfs/common/re2/re2_udf.cpp3
-rw-r--r--yql/essentials/udfs/common/re2/ya.make7
-rw-r--r--yql/essentials/udfs/common/reservoir_sampling/reservoir_udf.cpp3
-rw-r--r--yql/essentials/udfs/common/reservoir_sampling/ya.make3
-rw-r--r--yql/essentials/udfs/common/string/string_udf.cpp65
-rw-r--r--yql/essentials/udfs/common/string/ya.make9
-rw-r--r--yql/essentials/udfs/common/yson2/ya.make8
-rw-r--r--yql/essentials/udfs/common/yson2/yson2_udf.cpp65
-rw-r--r--yt/yql/providers/yt/opt/yql_yt_join.cpp8
-rw-r--r--yt/yql/providers/yt/opt/yql_yt_join.h4
-rw-r--r--yt/yql/providers/yt/provider/yql_yt_datasink_type_ann.cpp2
-rw-r--r--yt/yql/providers/yt/provider/yql_yt_join_impl.cpp6
-rw-r--r--yt/yql/providers/yt/provider/yql_yt_logical_optimize.cpp2
-rw-r--r--yt/yt/client/api/options.h4
-rw-r--r--yt/yt/client/api/rpc_proxy/transaction_impl.cpp27
-rw-r--r--yt/yt/client/api/rpc_proxy/transaction_impl.h3
-rw-r--r--yt/yt/client/driver/config.cpp3
-rw-r--r--yt/yt/client/driver/config.h4
-rw-r--r--yt/yt/client/driver/driver.cpp2
-rw-r--r--yt/yt/core/yson/protobuf_interop.cpp40
-rw-r--r--yt/yt/core/yson/public.h7
-rw-r--r--yt/yt/core/yson/unittests/protobuf_yson_ut.cpp17
-rw-r--r--yt/yt/core/ytree/attribute_filter.cpp44
-rw-r--r--yt/yt/core/ytree/attribute_filter.h12
-rw-r--r--yt/yt/core/ytree/ephemeral_node_factory.cpp28
-rw-r--r--yt/yt/core/ytree/unittests/attribute_filter_ut.cpp50
407 files changed, 15936 insertions, 2420 deletions
diff --git a/build/mapping.conf.json b/build/mapping.conf.json
index d14b2190fc4..6483820b6db 100644
--- a/build/mapping.conf.json
+++ b/build/mapping.conf.json
@@ -621,6 +621,7 @@
"12684893339": "{registry_endpoint}/12684893339",
"12727426752": "{registry_endpoint}/12727426752",
"12765030473": "{registry_endpoint}/12765030473",
+ "12778660629": "{registry_endpoint}/12778660629",
"5486713852": "{registry_endpoint}/5486713852",
"5514352253": "{registry_endpoint}/5514352253",
"5523579199": "{registry_endpoint}/5523579199",
@@ -2035,6 +2036,7 @@
"9606096462": "{registry_endpoint}/9606096462",
"9978231824": "{registry_endpoint}/9978231824",
"12331382363": "{registry_endpoint}/12331382363",
+ "12794858883": "{registry_endpoint}/12794858883",
"7503032594": "{registry_endpoint}/7503032594",
"9596164439": "{registry_endpoint}/9596164439",
"5631222854": "{registry_endpoint}/5631222854",
@@ -3037,6 +3039,7 @@
"12684893339": "devtools/ya/test/programs/test_tool/bin/test_tool for linux",
"12727426752": "devtools/ya/test/programs/test_tool/bin/test_tool for linux",
"12765030473": "devtools/ya/test/programs/test_tool/bin/test_tool for linux",
+ "12778660629": "devtools/ya/test/programs/test_tool/bin/test_tool for linux",
"5486713852": "devtools/ya/test/programs/test_tool/bin/test_tool for linux",
"5514352253": "devtools/ya/test/programs/test_tool/bin/test_tool for linux",
"5523579199": "devtools/ya/test/programs/test_tool/bin/test_tool for linux",
@@ -4451,6 +4454,7 @@
"9606096462": "[email protected]\n\npnpm module\n\n- [email protected]",
"9978231824": "[email protected]\n\npnpm module\n\n- [email protected]",
"12331382363": "[email protected]\n\npnpm module\n\n- [email protected]",
+ "12794858883": "[email protected]\n\npnpm module\n\n- [email protected]",
"7503032594": "[email protected]\n\npnpm module\n\n- [email protected]",
"9596164439": "[email protected]\n\npnpm module\n\n- [email protected]",
"5631222854": "python3 for darwin",
diff --git a/build/platform/test_tool/host.ya.make.inc b/build/platform/test_tool/host.ya.make.inc
index db6e35ab09f..e23ae9ddf1a 100644
--- a/build/platform/test_tool/host.ya.make.inc
+++ b/build/platform/test_tool/host.ya.make.inc
@@ -1,12 +1,12 @@
IF (HOST_OS_DARWIN AND HOST_ARCH_X86_64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765039259)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778677593)
ELSEIF (HOST_OS_DARWIN AND HOST_ARCH_ARM64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765037451)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778675385)
ELSEIF (HOST_OS_LINUX AND HOST_ARCH_X86_64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765041785)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778682489)
ELSEIF (HOST_OS_LINUX AND HOST_ARCH_AARCH64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765036448)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778673354)
ELSEIF (HOST_OS_WINDOWS AND HOST_ARCH_X86_64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765040553)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778679810)
ENDIF()
diff --git a/build/platform/test_tool/host_os.ya.make.inc b/build/platform/test_tool/host_os.ya.make.inc
index 665f091e86c..d65679840ec 100644
--- a/build/platform/test_tool/host_os.ya.make.inc
+++ b/build/platform/test_tool/host_os.ya.make.inc
@@ -1,12 +1,12 @@
IF (HOST_OS_DARWIN AND HOST_ARCH_X86_64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765029170)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778655677)
ELSEIF (HOST_OS_DARWIN AND HOST_ARCH_ARM64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765028287)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778653112)
ELSEIF (HOST_OS_LINUX AND HOST_ARCH_X86_64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765030473)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778660629)
ELSEIF (HOST_OS_LINUX AND HOST_ARCH_AARCH64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765027005)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778650528)
ELSEIF (HOST_OS_WINDOWS AND HOST_ARCH_X86_64)
- DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12765029766)
+ DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:12778657924)
ENDIF()
diff --git a/build/plugins/_dart_fields.py b/build/plugins/_dart_fields.py
index 177e6ccfbb7..3b7bd09c90b 100644
--- a/build/plugins/_dart_fields.py
+++ b/build/plugins/_dart_fields.py
@@ -869,6 +869,13 @@ class Requirements:
return serialize_list(requirements)
@classmethod
+ def from_unit_with_cpu(cls, unit, flat_args, spec_args):
+ requirements = get_values_list(unit, 'TEST_REQUIREMENTS_VALUE')
+ if [r for r in requirements if r and r.startswith("cpu:")]:
+ return serialize_list(requirements)
+ return serialize_list(requirements + ["cpu:4"])
+
+ @classmethod
def from_unit_with_full_network(cls, unit, flat_args, spec_args):
requirements = sorted(set(["network:full"] + get_values_list(unit, "TEST_REQUIREMENTS_VALUE")))
return serialize_list(requirements)
diff --git a/build/plugins/nots.py b/build/plugins/nots.py
index a24263215f3..f9085969aba 100644
--- a/build/plugins/nots.py
+++ b/build/plugins/nots.py
@@ -45,6 +45,7 @@ TS_LINT_DART_FIELDS = (
df.TsResources.value,
df.TsCheckType.value,
df.TsCheckHasCoverage.value,
+ df.Requirements.from_unit_with_cpu, # from macro REQUIREMENTS()
)
TS_TEST_DART_FIELDS = TS_LINT_DART_FIELDS + (
@@ -52,7 +53,6 @@ TS_TEST_DART_FIELDS = TS_LINT_DART_FIELDS + (
df.TestData.from_unit, # from macro DATA()
df.TestTimeout.from_unit, # from macro TIMEOUT()
df.Tag.from_unit, # from macro TAG()
- df.Requirements.from_unit, # from macro REQUIREMENTS()
df.TsTestForPath.value,
)
diff --git a/build/ya.conf.json b/build/ya.conf.json
index b1167d7f907..603aa6ddc96 100644
--- a/build/ya.conf.json
+++ b/build/ya.conf.json
@@ -68,22 +68,6 @@
}
}
},
- "clang-format": {
- "executable": {
- "clang-format": [
- "clang-format"
- ]
- },
- "formula": "build/platform/clang/clang-format/clang-format18.json"
- },
- "clang-format-16": {
- "executable": {
- "clang-format": [
- "clang-format"
- ]
- },
- "formula": "build/platform/clang/clang-format/clang-format16.json"
- },
"clang-format-18": {
"executable": {
"clang-format": [
@@ -571,50 +555,7 @@
],
"tools": {
"clang-format": {
- "bottle": "clang-format",
- "executable": "clang-format"
- }
- }
- },
- "clang-format-16": {
- "platforms": [
- {
- "default": true,
- "host": {
- "arch": "x86_64",
- "os": "LINUX"
- }
- },
- {
- "default": true,
- "host": {
- "arch": "aarch64",
- "os": "LINUX"
- }
- },
- {
- "default": true,
- "host": {
- "os": "WIN"
- }
- },
- {
- "default": true,
- "host": {
- "os": "DARWIN"
- }
- },
- {
- "default": true,
- "host": {
- "arch": "arm64",
- "os": "DARWIN"
- }
- }
- ],
- "tools": {
- "clang-format-16": {
- "bottle": "clang-format-16",
+ "bottle": "clang-format-18",
"executable": "clang-format"
}
}
@@ -1164,6 +1105,27 @@
{
"default": false,
"host": {
+ "os": "LINUX"
+ },
+ "target": {
+ "arch": "armv9a_grace",
+ "os": "LINUX"
+ }
+ },
+ {
+ "default": false,
+ "host": {
+ "arch": "aarch64",
+ "os": "LINUX"
+ },
+ "target": {
+ "arch": "armv9a_grace",
+ "os": "LINUX"
+ }
+ },
+ {
+ "default": false,
+ "host": {
"arch": "ppc64le",
"os": "LINUX"
},
@@ -1935,6 +1897,27 @@
{
"default": true,
"host": {
+ "os": "LINUX"
+ },
+ "target": {
+ "arch": "armv9a_grace",
+ "os": "LINUX"
+ }
+ },
+ {
+ "default": true,
+ "host": {
+ "arch": "aarch64",
+ "os": "LINUX"
+ },
+ "target": {
+ "arch": "armv9a_grace",
+ "os": "LINUX"
+ }
+ },
+ {
+ "default": true,
+ "host": {
"arch": "ppc64le",
"os": "LINUX"
},
@@ -2908,9 +2891,6 @@
"clang-format": {
"description": "Run Clang-Format source code formatter"
},
- "clang-format-16": {
- "description": "Run Clang-Format source code formatter (16 version)"
- },
"clang-format-18": {
"description": "Run Clang-Format source code formatter (18 version)"
},
diff --git a/build/ymake_conf.py b/build/ymake_conf.py
index 92b909f019e..21647f2c285 100755
--- a/build/ymake_conf.py
+++ b/build/ymake_conf.py
@@ -103,14 +103,16 @@ class Platform(object):
self.is_armv6 = self.arch in ('armv6hf',)
self.is_armv7 = self.arch in ('armv7', 'armv7a', 'armv7ahf', 'armv7a_neon', 'arm', 'armv7ahf_cortex_a7', 'armv7a_cortex_a9', 'armv7ahf_cortex_a35', 'armv7ahf_cortex_a53')
self.is_armv8 = self.arch in ('armv8', 'armv8a', 'arm64', 'aarch64', 'armv8a_cortex_a35', 'armv8a_cortex_a53')
+ self.is_armv9a = self.arch in ('armv9a', 'armv9a_grace')
self.is_armv8m = self.arch in ('armv8m_cortex_m33', 'armv8m_cortex_m23')
self.is_armv7em = self.arch in ('armv7em_cortex_m4', 'armv7em_cortex_m7')
self.is_arm64 = self.arch in ('arm64',)
- self.is_arm = self.is_armv6 or self.is_armv7 or self.is_armv8 or self.is_armv8m or self.is_armv7em
+ self.is_arm = self.is_armv6 or self.is_armv7 or self.is_armv8 or self.is_armv9a or self.is_armv8m or self.is_armv7em
self.is_armv7_neon = self.arch in ('armv7a_neon', 'armv7ahf', 'armv7ahf_cortex_a7', 'armv7a_cortex_a9', 'armv7ahf_cortex_a35', 'armv7ahf_cortex_a53')
self.is_armv6hf = self.arch in ('armv6hf',)
self.is_armv7hf = self.arch in ('armv7ahf', 'armv7ahf_cortex_a7', 'armv7ahf_cortex_a35', 'armv7ahf_cortex_a53')
self.is_armv5te = self.arch in ('armv5te_arm968e_s',)
+ self.is_grace = self.arch in ('armv9a_grace',)
self.is_arm_aml403 = self.arch == 'arm_aml403'
self.is_arm64_aml403 = self.arch == 'arm64_aml403'
self.is_arm_ats3089p = self.arch == 'arm_ats3089p'
@@ -163,7 +165,7 @@ class Platform(object):
self.is_armv5te or self.is_armv6 or self.is_armv7 or self.is_armv7em or self.is_armv8m or self.is_arm_aml403 or
self.is_riscv32 or self.is_nds32 or self.is_xtensa or self.is_tc32 or self.is_wasm32 or self.is_arm_ats3089p
)
- self.is_64_bit = self.is_x86_64 or self.is_armv8 or self.is_powerpc or self.is_wasm64 or self.is_riscv64 or self.is_arm64_aml403
+ self.is_64_bit = self.is_x86_64 or self.is_armv8 or self.is_armv9a or self.is_powerpc or self.is_wasm64 or self.is_riscv64 or self.is_arm64_aml403
assert self.is_32_bit or self.is_64_bit
assert not (self.is_32_bit and self.is_64_bit)
@@ -174,6 +176,7 @@ class Platform(object):
self.is_linux = self.os == 'linux' or 'yocto' in self.os
self.is_linux_x86_64 = self.is_linux and self.is_x86_64
self.is_linux_armv8 = self.is_linux and self.is_armv8
+ self.is_linux_armv9a = self.is_linux and self.is_armv9a
self.is_linux_armv7 = self.is_linux and self.is_armv7
self.is_linux_power8le = self.is_linux and self.is_power8le
self.is_linux_power9le = self.is_linux and self.is_power9le
@@ -244,6 +247,7 @@ class Platform(object):
(self.is_armv7, 'ARCH_ARM7'),
(self.is_armv7_neon, 'ARCH_ARM7_NEON'),
(self.is_armv8, 'ARCH_ARM64'),
+ (self.is_armv9a, 'ARCH_ARM64'),
(self.is_armv8m, 'ARCH_ARM8M'),
(self.is_armv7em, 'ARCH_ARM7EM'),
(self.is_armv5te, 'ARCH_ARM5TE'),
@@ -251,7 +255,10 @@ class Platform(object):
(self.is_arm_aml403, 'ARCH_ARM_AML403'),
(self.is_arm64_aml403, 'ARCH_ARM64_AML403'),
(self.is_arm_ats3089p, 'ARCH_ARM_ATS3089P'),
- (self.is_linux_armv8 or self.is_macos_arm64, 'ARCH_AARCH64'),
+ (self.is_linux_armv8, 'ARCH_AARCH64'),
+ (self.is_linux_armv9a, 'ARCH_AARCH64'),
+ (self.is_macos_arm64, 'ARCH_AARCH64'),
+ (self.is_grace, 'ARCH_ARMV9A_GRACE'),
(self.is_powerpc, 'ARCH_PPC64LE'),
(self.is_power8le, 'ARCH_POWER8LE'),
(self.is_power9le, 'ARCH_POWER9LE'),
@@ -528,7 +535,7 @@ def get_target_triple(target):
(target.is_freebsd and target.is_x86_64, 'x86_64-freebsd-unknown'),
(target.is_linux and target.is_x86_64, 'x86_64-linux-gnu'),
- (target.is_linux and target.is_armv8, 'aarch64-linux-gnu'),
+ (target.is_linux and (target.is_armv8 or target.is_armv9a), 'aarch64-linux-gnu'),
(target.is_linux and target.is_armv6 and target.armv6_float_abi == 'hard', 'armv6-linux-gnueabihf'),
(target.is_linux and target.is_armv7 and target.armv7_float_abi == 'hard', 'armv7-linux-gnueabihf'),
(target.is_linux and target.is_armv7 and target.armv7_float_abi == 'softfp', 'armv7-linux-gnueabi'),
@@ -1344,6 +1351,20 @@ class GnuToolchain(Toolchain):
target_flags = select(default=[], selectors=[
(target.is_linux and target.is_power8le, ['-mcpu=power8', '-mtune=power8', '-maltivec']),
(target.is_linux and target.is_power9le, ['-mcpu=power9', '-mtune=power9', '-maltivec']),
+ (
+ target.is_linux and target.is_grace and (
+ (self.tc.is_clang and self.tc.version_at_least(16)) or
+ (self.tc.is_gcc and self.tc.version_at_least(13))
+ ),
+ ['-mcpu=neoverse-v2+norng+crypto+sve2-sm4+sve2-aes+sve2-sha3'],
+ ),
+ (
+ target.is_linux and target.is_grace,
+ [
+ '-march=armv9-a+crypto+sve2+sve2-bitperm+bf16+i8mm+fp16fml+sve2-aes+sve2-sha3+sve2-sm4+sha3+sm4+crc+dotprod+lse+rcpc+rdm+nossbs+noras+norng',
+ '-mtune=neoverse-n2',
+ ],
+ ),
(target.is_linux and target.is_armv8, ['-march=armv8-a']),
(target.is_macos, ['-mmacosx-version-min={}'.format(MACOS_VERSION_MIN)]),
(target.is_ios and not target.is_iossim, ['-mios-version-min={}'.format(IOS_VERSION_MIN)]),
@@ -1377,7 +1398,7 @@ class GnuToolchain(Toolchain):
self.setup_tools(project='build/platform/binutils', var='$BINUTILS_ROOT_RESOURCE_GLOBAL', bin='x86_64-linux-gnu/bin', ldlibs=None)
elif target.is_powerpc:
self.setup_tools(project='build/platform/linux_sdk', var='$OS_SDK_ROOT_RESOURCE_GLOBAL', bin='usr/bin', ldlibs='usr/x86_64-linux-gnu/powerpc64le-linux-gnu/lib')
- elif target.is_armv8:
+ elif target.is_armv8 or target.is_armv9a:
self.setup_tools(project='build/platform/linux_sdk', var='$OS_SDK_ROOT_RESOURCE_GLOBAL', bin='usr/bin', ldlibs='usr/lib/x86_64-linux-gnu')
if target.is_yocto:
@@ -2589,14 +2610,14 @@ class Cuda(object):
return False
if host != target:
- if not (host.is_linux_x86_64 and target.is_linux_armv8):
+ if not (host.is_linux_x86_64 and (target.is_linux_armv8 or target.is_linux_armv9a)):
return False
if not self.cuda_version.from_user:
return False
if self.cuda_version.value in ('11.4', '11.8', '12.1', '12.2', '12.6', '12.6.2', '12.6.3', '12.8', '12.9', '13.0'):
return True
- elif self.cuda_version.value in ('10.2', '11.4.19') and target.is_linux_armv8:
+ elif self.cuda_version.value in ('10.2', '11.4.19') and (target.is_linux_armv8 or target.is_linux_armv9a):
return True
else:
raise ConfigureError('CUDA version {} is not supported in Arcadia'.format(self.cuda_version.value))
@@ -2731,6 +2752,7 @@ class Cuda(object):
return select((
(host.is_linux_x86_64 and target.is_linux_x86_64, '$CUDA_HOST_TOOLCHAIN_RESOURCE_GLOBAL/bin/clang'),
(host.is_linux_x86_64 and target.is_linux_armv8, '$CUDA_HOST_TOOLCHAIN_RESOURCE_GLOBAL/bin/clang'),
+ (host.is_linux_x86_64 and target.is_linux_armv9a, '$CUDA_HOST_TOOLCHAIN_RESOURCE_GLOBAL/bin/clang'),
))
def cuda_windows_host_compiler(self):
diff --git a/contrib/libs/croaring/.yandex_meta/override.nix b/contrib/libs/croaring/.yandex_meta/override.nix
index 0bc39a1316f..534abb9060a 100644
--- a/contrib/libs/croaring/.yandex_meta/override.nix
+++ b/contrib/libs/croaring/.yandex_meta/override.nix
@@ -1,12 +1,12 @@
pkgs: attrs: with pkgs; with attrs; rec {
pname = "croaring";
- version = "4.7.1";
+ version = "4.7.2";
src = fetchFromGitHub {
owner = "RoaringBitmap";
repo = "CRoaring";
rev = "v${version}";
- hash = "sha256-FZP+RTV4pcj9pzDq3G2+sWeJnkh9WnW3Atd0CC9zDCk=";
+ hash = "sha256-WSEMkXkR6diE5CV3gQ3tUAodLqNsWmBmrGyXOKg4CJA=";
};
patches = [];
diff --git a/contrib/libs/croaring/AGENTS.md b/contrib/libs/croaring/AGENTS.md
index ca439b42222..f85c04cef49 100644
--- a/contrib/libs/croaring/AGENTS.md
+++ b/contrib/libs/croaring/AGENTS.md
@@ -14,10 +14,10 @@ The library's documented contract is explicit and consistent across the public A
Relevant documentation locations:
-- [include/roaring/roaring.h](/Users/dlemire/CVS/github/CRoaring/include/roaring/roaring.h) (around lines 671-678, 726-738, 1067-1082): detailed comments on `roaring_bitmap_*_deserialize*` and `roaring_bitmap_internal_validate`.
-- [include/roaring/roaring64.h](/Users/dlemire/CVS/github/CRoaring/include/roaring/roaring64.h) (around lines 615-631, 365-376): equivalent guidance for 64-bit bitmaps.
-- [README.md](/Users/dlemire/CVS/github/CRoaring/README.md) (lines 357-375, 645-678): API summary and example code showing the required validate-after-deserialize pattern for untrusted input.
+- include/roaring/roaring.h: detailed comments on `roaring_bitmap_*_deserialize*` and `roaring_bitmap_internal_validate`.
+- include/roaring/roaring64.h: equivalent guidance for 64-bit bitmaps.
+- README.md: API summary and example code showing the required validate-after-deserialize pattern for untrusted input.
The robust deserialization tests in `tests/robust_deserialization_unit.c` and fuzzing harnesses exist precisely to ensure the safe deserializers and the validators behave correctly under adversarial input. A report that treats "deserializing attacker-controlled bytes and then using the result without validation" as a bug in the deserializer is a misunderstanding of the stated API contract.
-When triaging such reports, point to the validation requirement in the function documentation and close as "not a bug / user error / documented behavior." \ No newline at end of file
+When triaging such reports, point to the validation requirement in the function documentation and close as "not a bug / user error / documented behavior."
diff --git a/contrib/libs/croaring/cpp/roaring/roaring.hh b/contrib/libs/croaring/cpp/roaring/roaring.hh
index d039a367cf8..f1af9aca589 100644
--- a/contrib/libs/croaring/cpp/roaring/roaring.hh
+++ b/contrib/libs/croaring/cpp/roaring/roaring.hh
@@ -666,8 +666,8 @@ class Roaring {
* many, many bytes could be read. See also readSafe.
*
* The function may throw std::runtime_error if a bitmap could not be read.
- * Note that even if it does not throw, the bitmap could still be unusable if
- * the loaded data does not match the portable Roaring specification: you
+ * Note that even if it does not throw, the bitmap could still be unusable
+ * if the loaded data does not match the portable Roaring specification: you
* should ensure that the data you load come from a serialized bitmap.
*/
static Roaring read(const char *buf, bool portable = true) {
@@ -699,8 +699,8 @@ class Roaring {
* method).
*
* The function may throw std::runtime_error if a bitmap could not be read.
- * Note that even if it does not throw, the bitmap could still be unusable if
- * the loaded data does not match the portable Roaring specification: you
+ * Note that even if it does not throw, the bitmap could still be unusable
+ * if the loaded data does not match the portable Roaring specification: you
* should ensure that the data you load come from a serialized bitmap.
*/
static Roaring readSafe(const char *buf, size_t maxbytes) {
diff --git a/contrib/libs/croaring/include/roaring/roaring_version.h b/contrib/libs/croaring/include/roaring/roaring_version.h
index eb5b1b358cb..cfe16193be5 100644
--- a/contrib/libs/croaring/include/roaring/roaring_version.h
+++ b/contrib/libs/croaring/include/roaring/roaring_version.h
@@ -2,11 +2,11 @@
// /include/roaring/roaring_version.h automatically generated by release.py, do not change by hand
#ifndef ROARING_INCLUDE_ROARING_VERSION
#define ROARING_INCLUDE_ROARING_VERSION
-#define ROARING_VERSION "4.7.1"
+#define ROARING_VERSION "4.7.2"
enum {
ROARING_VERSION_MAJOR = 4,
ROARING_VERSION_MINOR = 7,
- ROARING_VERSION_REVISION = 1
+ ROARING_VERSION_REVISION = 2
};
#endif // ROARING_INCLUDE_ROARING_VERSION
// clang-format on \ No newline at end of file
diff --git a/contrib/libs/croaring/src/bitset_util.c b/contrib/libs/croaring/src/bitset_util.c
index 6bc12b44b49..c75bfdf1520 100644
--- a/contrib/libs/croaring/src/bitset_util.c
+++ b/contrib/libs/croaring/src/bitset_util.c
@@ -576,6 +576,7 @@ const uint8_t vbmi2_table[64] = {
size_t bitset_extract_setbits_avx512(const uint64_t *words, size_t length,
uint32_t *vout, size_t outcapacity,
uint32_t base) {
+ if (outcapacity == 0) return 0;
uint32_t *out = (uint32_t *)vout;
uint32_t *initout = out;
uint32_t *safeout = out + outcapacity;
@@ -632,6 +633,7 @@ size_t bitset_extract_setbits_avx512(const uint64_t *words, size_t length,
size_t bitset_extract_setbits_avx512_uint16(const uint64_t *array,
size_t length, uint16_t *vout,
size_t capacity, uint16_t base) {
+ if (capacity == 0) return 0;
uint16_t *out = (uint16_t *)vout;
uint16_t *initout = out;
uint16_t *safeout = vout + capacity;
@@ -683,6 +685,7 @@ CROARING_TARGET_AVX2
size_t bitset_extract_setbits_avx2(const uint64_t *words, size_t length,
uint32_t *out, size_t outcapacity,
uint32_t base) {
+ if (outcapacity == 0) return 0;
uint32_t *initout = out;
__m256i baseVec = _mm256_set1_epi32(base - 1);
__m256i incVec = _mm256_set1_epi32(64);
@@ -786,6 +789,7 @@ CROARING_TARGET_AVX2
size_t bitset_extract_setbits_sse_uint16(const uint64_t *words, size_t length,
uint16_t *out, size_t outcapacity,
uint16_t base) {
+ if (outcapacity == 0) return 0;
uint16_t *initout = out;
__m128i baseVec = _mm_set1_epi16(base - 1);
__m128i incVec = _mm_set1_epi16(64);
diff --git a/contrib/libs/croaring/ya.make b/contrib/libs/croaring/ya.make
index 0f8b7adfccb..597a865b41c 100644
--- a/contrib/libs/croaring/ya.make
+++ b/contrib/libs/croaring/ya.make
@@ -10,9 +10,9 @@ LICENSE(
LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
-VERSION(4.7.1)
+VERSION(4.7.2)
-ORIGINAL_SOURCE(https://github.com/RoaringBitmap/CRoaring/archive/v4.7.1.tar.gz)
+ORIGINAL_SOURCE(https://github.com/RoaringBitmap/CRoaring/archive/v4.7.2.tar.gz)
ADDINCL(
GLOBAL contrib/libs/croaring/include
diff --git a/contrib/libs/liburing/.yandex_meta/devtools.licenses.report b/contrib/libs/liburing/.yandex_meta/devtools.licenses.report
index f1fad73693c..ee4d3c57e63 100644
--- a/contrib/libs/liburing/.yandex_meta/devtools.licenses.report
+++ b/contrib/libs/liburing/.yandex_meta/devtools.licenses.report
@@ -52,21 +52,21 @@ BELONGS ya.make
Match type : REFERENCE
Links : https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/unknown-license-reference.LICENSE
Files with this license:
- README [108:108]
+ README [116:116]
Scancode info:
Original SPDX id: GPL-1.0-or-later
Score : 50.00
Match type : REFERENCE
Links : http://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html, https://spdx.org/licenses/GPL-1.0-or-later
Files with this license:
- README [108:108]
+ README [116:116]
Scancode info:
Original SPDX id: Linux-syscall-note
Score : 50.00
Match type : REFERENCE
Links : https://spdx.org/licenses/Linux-syscall-note
Files with this license:
- README [108:108]
+ README [116:116]
KEEP MIT 1eaf467c6d00edd4a5876ad437b67edf
BELONGS ya.make
@@ -107,6 +107,7 @@ BELONGS ya.make
test/a0908ae19763.c [2:2]
test/a4c0b3decb33.c [2:2]
test/accept-link.c [2:2]
+ test/accept-mshot-stress.c [2:2]
test/accept-non-empty.c [2:2]
test/accept-reuse.c [2:2]
test/accept-test.c [2:2]
@@ -115,9 +116,15 @@ BELONGS ya.make
test/b19062a56726.c [2:2]
test/b5837bd5311d.c [2:2]
test/bind-listen.c [2:2]
+ test/buf-ring-mshot.c [2:2]
test/buf-ring-nommap.c [2:2]
test/buf-ring-put.c [2:2]
+ test/buf-ring-stress.c [2:2]
+ test/buf-ring-upgrade.c [2:2]
test/buf-ring.c [2:2]
+ test/cancel-fd-userdata.c [2:2]
+ test/cancel-race.c [2:2]
+ test/cbpf_filter.c [2:2]
test/ce593a6c480a.c [2:2]
test/cmd-discard.c [2:2]
test/conn-unreach.c [2:2]
@@ -126,6 +133,7 @@ BELONGS ya.make
test/coredump.c [2:2]
test/cq-full.c [2:2]
test/cq-overflow.c [2:2]
+ test/cq-peek-batch-mixed.c [2:2]
test/cq-peek-batch.c [2:2]
test/cq-ready.c [2:2]
test/cq-size.c [2:2]
@@ -187,9 +195,11 @@ BELONGS ya.make
test/io_uring_setup.c [2:2]
test/iopoll-leak.c [2:2]
test/iopoll-overflow.c [2:2]
+ test/iopoll-sync.c [2:2]
test/iopoll.c [2:2]
test/iowait.c [2:2]
test/kallsyms.c [2:2]
+ test/large-resize.c [2:2]
test/lfs-openat-write.c [2:2]
test/lfs-openat.c [2:2]
test/link-timeout.c [2:2]
@@ -209,6 +219,8 @@ BELONGS ya.make
test/no-mmap-inval.c [2:2]
test/nolibc.c [2:2]
test/nop-all-sizes.c [2:2]
+ test/nop-fixed-file-leak.c [2:2]
+ test/nop-flags.c [2:2]
test/nop.c [2:2]
test/nop32-overflow.c [2:2]
test/nop32.c [2:2]
@@ -227,6 +239,7 @@ BELONGS ya.make
test/poll-link.c [2:2]
test/poll-many.c [2:2]
test/poll-mshot-update.c [2:2]
+ test/poll-mshot-wake.c [2:2]
test/poll-race-mshot.c [2:2]
test/poll-race.c [2:2]
test/poll-ring.c [2:2]
@@ -236,16 +249,20 @@ BELONGS ya.make
test/pollfree.c [2:2]
test/probe.c [2:2]
test/read-before-exit.c [2:2]
+ test/read-inc-buf-more.c [2:2]
test/read-inc-file.c [2:2]
test/read-mshot-empty.c [2:2]
test/read-mshot-stdin.c [2:2]
test/read-mshot.c [2:2]
test/read-write.c [2:2]
+ test/recv-bundle-buf-len.c [2:2]
test/recv-bundle-short-ooo.c [2:2]
test/recv-inc-ooo.c [2:2]
test/recv-msgall-stream.c [2:2]
test/recv-msgall.c [2:2]
+ test/recv-mshot-drain.c [2:2]
test/recv-mshot-fair.c [2:2]
+ test/recvmsg-inc-tail.c [2:2]
test/recvsend_bundle-inc.c [2:2]
test/recvsend_bundle.c [2:2]
test/reg-fd-only.c [2:2]
@@ -256,6 +273,7 @@ BELONGS ya.make
test/regbuf-merge.c [2:2]
test/register-restrictions.c [2:2]
test/rename.c [2:2]
+ test/resize-mmap-fail.c [2:2]
test/resize-rings.c [2:2]
test/ring-leak.c [2:2]
test/ring-leak2.c [2:2]
@@ -292,6 +310,7 @@ BELONGS ya.make
test/sq-poll-share.c [2:2]
test/sq-space_left.c [2:2]
test/sqe-mixed-bad-wrap.c [2:2]
+ test/sqe-mixed-boundary.c [2:2]
test/sqe-mixed-nop.c [2:2]
test/sqe-mixed-uring_cmd.c [2:2]
test/sqpoll-disable-exit.c [2:2]
@@ -305,9 +324,11 @@ BELONGS ya.make
test/submit-reuse.c [2:2]
test/symlink.c [2:2]
test/sync-cancel.c [2:2]
+ test/task-restrict.c [2:2]
test/teardowns.c [2:2]
test/test.h [1:1]
test/thread-exit.c [2:2]
+ test/timens-abs-timer.c [2:2]
test/timeout-new.c [2:2]
test/timeout.c [2:2]
test/timerfd-short-read.c [2:2]
@@ -364,6 +385,7 @@ BELONGS ya.make
Links : http://www.gnu.org/licenses/gpl-2.0.html, http://www.gnu.org/licenses/gpl-2.0.txt, https://spdx.org/licenses/GPL-2.0-only
Files with this license:
src/include/liburing/io_uring.h [1:1]
+ src/include/liburing/io_uring/bpf_filter.h [1:1]
src/include/liburing/io_uring/query.h [1:1]
Scancode info:
Original SPDX id: Linux-syscall-note
@@ -372,6 +394,7 @@ BELONGS ya.make
Links : https://spdx.org/licenses/Linux-syscall-note
Files with this license:
src/include/liburing/io_uring.h [1:1]
+ src/include/liburing/io_uring/bpf_filter.h [1:1]
src/include/liburing/io_uring/query.h [1:1]
Scancode info:
Original SPDX id: MIT
@@ -380,6 +403,7 @@ BELONGS ya.make
Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
Files with this license:
src/include/liburing/io_uring.h [1:1]
+ src/include/liburing/io_uring/bpf_filter.h [1:1]
src/include/liburing/io_uring/query.h [1:1]
KEEP MIT 8384d75c38c570f3edb87cf9f64f2ec2
@@ -394,14 +418,17 @@ BELONGS ya.make
Files with this license:
test/close-opath.c [2:2]
test/defer-taskrun.c [2:2]
+ test/file-alloc-range-hint.c [2:2]
+ test/mshot-shutdown-race.c [2:2]
test/pipe-bug.c [2:2]
+ test/pipe-direct-fixed.c [2:2]
test/pipe.c [2:2]
test/poll-mshot-overflow.c [2:2]
test/recv-multishot.c [2:2]
KEEP "(GPL-2.0-only WITH Linux-syscall-note OR MIT)" 9566044df50ee1c37e39ed99e7926750
BELONGS ya.make
-FILE_INCLUDE COPYING.GPL found in files: README at line 109
+FILE_INCLUDE COPYING.GPL found in files: README at line 117
License text:
COPYING.GPL and <https://spdx.org/licenses/Linux-syscall-note.html>.
Scancode info:
@@ -410,14 +437,14 @@ FILE_INCLUDE COPYING.GPL found in files: README at line 109
Match type : REFERENCE
Links : http://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html, https://spdx.org/licenses/GPL-1.0-or-later
Files with this license:
- README [109:109]
+ README [117:117]
Scancode info:
Original SPDX id: Linux-syscall-note
Score : 95.00
Match type : REFERENCE
Links : https://spdx.org/licenses/Linux-syscall-note
Files with this license:
- README [109:109]
+ README [117:117]
KEEP "(LGPL-2.1-only OR MIT)" 9d1d95673c01a94e06648bf72ecda12d
BELONGS ya.make
@@ -446,11 +473,11 @@ BELONGS ya.make
Match type : REFERENCE
Links : https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/unknown-license-reference.LICENSE
Files with this license:
- README [106:106]
+ README [114:114]
Scancode info:
Original SPDX id: LGPL-2.0-or-later
Score : 75.00
Match type : REFERENCE
Links : http://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html, http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html, https://spdx.org/licenses/LGPL-2.0-or-later
Files with this license:
- README [106:106]
+ README [114:114]
diff --git a/contrib/libs/liburing/.yandex_meta/override.nix b/contrib/libs/liburing/.yandex_meta/override.nix
index e2b0bfffc79..0c80703a674 100644
--- a/contrib/libs/liburing/.yandex_meta/override.nix
+++ b/contrib/libs/liburing/.yandex_meta/override.nix
@@ -1,12 +1,12 @@
pkgs: attrs: with pkgs; with attrs; rec {
name = "liburing";
- version = "2.14";
+ version = "2.15";
src = fetchFromGitHub {
owner = "axboe";
repo = "liburing";
rev = "liburing-${version}";
- hash = "sha256-bSq4M28JRND4bdaIv/KXcCDB35cYM7gra1GVO3poWfc=";
+ hash = "sha256-oBNu5DI2RMk0BPm6NT8qaYuyhk7+KIpFSsBsVwI7BO8=";
};
buildPhase = ''
diff --git a/contrib/libs/liburing/README b/contrib/libs/liburing/README
index e14fed890c4..dc1e97af808 100644
--- a/contrib/libs/liburing/README
+++ b/contrib/libs/liburing/README
@@ -75,6 +75,14 @@ Building liburing
See './configure --help' for more information about build config options.
+Out-of-source builds are also supported, keeping the source tree clean, and
+allowing you to have different configurations side-by-side:
+
+ mkdir build && cd build
+ ../configure
+ make -j$(nproc)
+ sudo make install
+
FFI support
-----------
diff --git a/contrib/libs/liburing/src/arch/aarch64/lib.h b/contrib/libs/liburing/src/arch/aarch64/lib.h
index 41bcfc957d1..285489d129d 100644
--- a/contrib/libs/liburing/src/arch/aarch64/lib.h
+++ b/contrib/libs/liburing/src/arch/aarch64/lib.h
@@ -6,6 +6,18 @@
#include <elf.h>
#include "../../syscall.h"
+#ifndef CONFIG_NOLIBC
+#include <unistd.h>
+
+static inline long __get_page_size(void)
+{
+ long ret = sysconf(_SC_PAGESIZE);
+
+ if (ret < 0)
+ ret = 4096;
+ return ret;
+}
+#else
static inline long __get_page_size(void)
{
Elf64_Off buf[2];
@@ -32,6 +44,7 @@ static inline long __get_page_size(void)
__sys_close(fd);
return ret;
}
+#endif
static inline long get_page_size(void)
{
diff --git a/contrib/libs/liburing/src/arch/syscall-defs.h b/contrib/libs/liburing/src/arch/syscall-defs.h
index 44235027c26..fda2dfd8a69 100644
--- a/contrib/libs/liburing/src/arch/syscall-defs.h
+++ b/contrib/libs/liburing/src/arch/syscall-defs.h
@@ -48,12 +48,12 @@ static inline int __sys_madvise(void *addr, size_t length, int advice)
static inline int __sys_getrlimit(int resource, struct rlimit *rlim)
{
- return (int) __do_syscall2(__NR_getrlimit, resource, rlim);
+ return (int) __do_syscall4(__NR_prlimit64, 0, resource, NULL, rlim);
}
static inline int __sys_setrlimit(int resource, const struct rlimit *rlim)
{
- return (int) __do_syscall2(__NR_setrlimit, resource, rlim);
+ return (int) __do_syscall4(__NR_prlimit64, 0, resource, rlim, NULL);
}
static inline int __sys_close(int fd)
diff --git a/contrib/libs/liburing/src/include/liburing.h b/contrib/libs/liburing/src/include/liburing.h
index 8d45d40084a..0188937b0de 100644
--- a/contrib/libs/liburing/src/include/liburing.h
+++ b/contrib/libs/liburing/src/include/liburing.h
@@ -13,10 +13,12 @@
#include <fcntl.h>
#include <sched.h>
#include <linux/swab.h>
+#include <linux/filter.h>
#include <sys/wait.h>
#include "liburing/compat.h"
#include "liburing/io_uring.h"
#include "liburing/io_uring/query.h"
+#include "liburing/io_uring/bpf_filter.h"
#include "liburing/io_uring_version.h"
#ifndef uring_unlikely
@@ -212,7 +214,7 @@ void io_uring_free_probe(struct io_uring_probe *probe) LIBURING_NOEXCEPT;
IOURINGINLINE int io_uring_opcode_supported(const struct io_uring_probe *p,
int op) LIBURING_NOEXCEPT
{
- if (op > p->last_op)
+ if (op < 0 || op > p->last_op)
return 0;
return (p->ops[op].flags & IO_URING_OP_SUPPORTED) != 0;
}
@@ -352,10 +354,18 @@ int io_uring_unregister_napi(struct io_uring *ring, struct io_uring_napi *napi)
LIBURING_NOEXCEPT;
int io_uring_register_ifq(struct io_uring *ring,
struct io_uring_zcrx_ifq_reg *reg) LIBURING_NOEXCEPT;
+int io_uring_register_zcrx_ctrl(struct io_uring *ring, struct zcrx_ctrl *ctrl)
+ LIBURING_NOEXCEPT;
int io_uring_register_clock(struct io_uring *ring,
struct io_uring_clock_register *arg)
LIBURING_NOEXCEPT;
+int io_uring_register_bpf_filter(struct io_uring *ring,
+ struct io_uring_bpf *bpf) LIBURING_NOEXCEPT;
+int io_uring_register_bpf_filter_task(struct io_uring_bpf *bpf)
+ LIBURING_NOEXCEPT;
+
+int io_uring_register_query(struct io_uring_query_hdr *query) LIBURING_NOEXCEPT;
int io_uring_get_events(struct io_uring *ring) LIBURING_NOEXCEPT;
int io_uring_submit_and_get_events(struct io_uring *ring) LIBURING_NOEXCEPT;
@@ -426,7 +436,9 @@ IOURINGINLINE unsigned io_uring_cqe_shift(const struct io_uring *ring)
IOURINGINLINE unsigned io_uring_cqe_nr(const struct io_uring_cqe *cqe)
{
- return 1U << !!(cqe->flags & IORING_CQE_F_32);
+ const unsigned int shift = !!(cqe->flags & IORING_CQE_F_32);
+
+ return 1U << shift;
}
struct io_uring_cqe_iter {
@@ -437,7 +449,7 @@ struct io_uring_cqe_iter {
unsigned tail;
};
-_LOCAL_INLINE struct io_uring_cqe_iter
+IOURINGINLINE struct io_uring_cqe_iter
io_uring_cqe_iter_init(const struct io_uring *ring)
LIBURING_NOEXCEPT
{
@@ -451,7 +463,7 @@ io_uring_cqe_iter_init(const struct io_uring *ring)
};
}
-_LOCAL_INLINE bool io_uring_cqe_iter_next(struct io_uring_cqe_iter *iter,
+IOURINGINLINE bool io_uring_cqe_iter_next(struct io_uring_cqe_iter *iter,
struct io_uring_cqe **cqe)
LIBURING_NOEXCEPT
{
@@ -1187,9 +1199,17 @@ IOURINGINLINE struct io_uring_recvmsg_out *
io_uring_recvmsg_validate(void *buf, int buf_len, struct msghdr *msgh)
LIBURING_NOEXCEPT
{
- unsigned long header = msgh->msg_controllen + msgh->msg_namelen +
- sizeof(struct io_uring_recvmsg_out);
- if (buf_len < 0 || (unsigned long)buf_len < header)
+ unsigned long ulen = (unsigned long)(unsigned int)buf_len;
+ unsigned long hdr = sizeof(struct io_uring_recvmsg_out);
+ unsigned long namelen = msgh->msg_namelen;
+ unsigned long controllen = msgh->msg_controllen;
+
+ if (buf_len < 0 || ulen < hdr)
+ return NULL;
+ /* check each addition separately to avoid integer overflow */
+ if (namelen > ulen - hdr)
+ return NULL;
+ if (controllen > ulen - hdr - namelen)
return NULL;
return (struct io_uring_recvmsg_out *)buf;
}
@@ -1249,8 +1269,12 @@ io_uring_recvmsg_payload_length(struct io_uring_recvmsg_out *o,
{
unsigned long payload_start, payload_end;
+ if (buf_len < 0)
+ return 0;
payload_start = (unsigned long) io_uring_recvmsg_payload(o, msgh);
payload_end = (unsigned long) o + buf_len;
+ if (payload_start >= payload_end)
+ return 0;
return (unsigned int) (payload_end - payload_start);
}
@@ -1843,7 +1867,7 @@ out:
* "official" versions of this, io_uring_peek_cqe(), io_uring_wait_cqe(),
* or io_uring_wait_cqes*().
*/
-_LOCAL_INLINE int __io_uring_peek_cqe(struct io_uring *ring,
+IOURINGINLINE int __io_uring_peek_cqe(struct io_uring *ring,
struct io_uring_cqe **cqe_ptr,
unsigned *nr_available)
LIBURING_NOEXCEPT
@@ -1857,11 +1881,13 @@ _LOCAL_INLINE int __io_uring_peek_cqe(struct io_uring *ring,
do {
unsigned tail = io_uring_smp_load_acquire(ring->cq.ktail);
- /**
- * A load_acquire on the head prevents reordering with the
- * cqe load below, ensuring that we see the correct cq entry.
+ /*
+ * The acquire ordering on the tail load pairs with the kernel
+ * side publishing CQEs, and guarantees the contents of any
+ * entry in [head, tail). The CQ head is only ever written by
+ * the application, so a plain load is sufficient.
*/
- unsigned head = io_uring_smp_load_acquire(ring->cq.khead);
+ unsigned head = *ring->cq.khead;
cqe = NULL;
available = tail - head;
@@ -1869,8 +1895,16 @@ _LOCAL_INLINE int __io_uring_peek_cqe(struct io_uring *ring,
break;
cqe = &ring->cq.cqes[(head & mask) << shift];
- if (!io_uring_skip_cqe(ring, cqe, &err))
+ if (!io_uring_skip_cqe(ring, cqe, &err)) {
+ /*
+ * If an error was set, the CQE was an internal
+ * timeout and has already been consumed - don't
+ * return a pointer to it.
+ */
+ if (err)
+ cqe = NULL;
break;
+ }
cqe = NULL;
} while (1);
@@ -1888,8 +1922,20 @@ IOURINGINLINE int io_uring_peek_cqe(struct io_uring *ring,
struct io_uring_cqe **cqe_ptr)
LIBURING_NOEXCEPT
{
- if (!__io_uring_peek_cqe(ring, cqe_ptr, NULL) && *cqe_ptr)
- return 0;
+ if (!__io_uring_peek_cqe(ring, cqe_ptr, NULL)) {
+ if (*cqe_ptr)
+ return 0;
+ /*
+ * If the CQ is empty and there's nothing the kernel could
+ * flush to it (no IOPOLL completions to reap, no overflown
+ * CQEs, no pending task work), avoid the round trip into
+ * the full get_cqe machinery.
+ */
+ if (!(ring->flags & IORING_SETUP_IOPOLL) &&
+ !(IO_URING_READ_ONCE(*ring->sq.kflags) &
+ (IORING_SQ_CQ_OVERFLOW | IORING_SQ_TASKRUN)))
+ return -EAGAIN;
+ }
return io_uring_wait_cqe_nr(ring, cqe_ptr, 0);
}
diff --git a/contrib/libs/liburing/src/include/liburing/io_uring.h b/contrib/libs/liburing/src/include/liburing/io_uring.h
index 2696b43d949..b9ec1ebfdb6 100644
--- a/contrib/libs/liburing/src/include/liburing/io_uring.h
+++ b/contrib/libs/liburing/src/include/liburing/io_uring.h
@@ -184,7 +184,8 @@ enum io_uring_sqe_flags_bit {
/*
* If COOP_TASKRUN is set, get notified if task work is available for
* running and a kernel transition would be needed to run it. This sets
- * IORING_SQ_TASKRUN in the sq ring flags. Not valid with COOP_TASKRUN.
+ * IORING_SQ_TASKRUN in the sq ring flags. Not valid without COOP_TASKRUN
+ * or DEFER_TASKRUN.
*/
#define IORING_SETUP_TASKRUN_FLAG (1U << 9)
#define IORING_SETUP_SQE128 (1U << 10) /* SQEs are 128 byte */
@@ -332,6 +333,10 @@ enum io_uring_op {
/*
* sqe->timeout_flags
+ *
+ * IORING_TIMEOUT_IMMEDIATE_ARG: If set, sqe->addr stores the timeout
+ * value in nanoseconds instead of
+ * pointing to a timespec.
*/
#define IORING_TIMEOUT_ABS (1U << 0)
#define IORING_TIMEOUT_UPDATE (1U << 1)
@@ -340,6 +345,7 @@ enum io_uring_op {
#define IORING_LINK_TIMEOUT_UPDATE (1U << 4)
#define IORING_TIMEOUT_ETIME_SUCCESS (1U << 5)
#define IORING_TIMEOUT_MULTISHOT (1U << 6)
+#define IORING_TIMEOUT_IMMEDIATE_ARG (1U << 7)
#define IORING_TIMEOUT_CLOCK_MASK (IORING_TIMEOUT_BOOTTIME | IORING_TIMEOUT_REALTIME)
#define IORING_TIMEOUT_UPDATE_MASK (IORING_TIMEOUT_UPDATE | IORING_LINK_TIMEOUT_UPDATE)
/*
@@ -697,6 +703,12 @@ enum io_uring_register_op {
/* query various aspects of io_uring, see linux/io_uring/query.h */
IORING_REGISTER_QUERY = 35,
+ /* auxiliary zcrx configuration, see enum zcrx_ctrl_op */
+ IORING_REGISTER_ZCRX_CTRL = 36,
+
+ /* register bpf filtering programs */
+ IORING_REGISTER_BPF_FILTER = 37,
+
/* this goes last */
IORING_REGISTER_LAST,
@@ -802,6 +814,13 @@ struct io_uring_restriction {
__u32 resv2[3];
};
+struct io_uring_task_restriction {
+ __u16 flags;
+ __u16 nr_res;
+ __u32 resv[3];
+ struct io_uring_restriction restrictions[0];
+};
+
struct io_uring_clock_register {
__u32 clockid;
__u32 __resv[3];
@@ -872,7 +891,8 @@ struct io_uring_buf_reg {
__u32 ring_entries;
__u16 bgid;
__u16 flags;
- __u64 resv[3];
+ __u32 min_left;
+ __u32 resv[5];
};
/* argument for IORING_REGISTER_PBUF_STATUS */
@@ -1033,6 +1053,24 @@ struct io_uring_zcrx_area_reg {
__u64 __resv2[2];
};
+enum zcrx_reg_flags {
+ ZCRX_REG_IMPORT = 1,
+ /*
+ * Register a zcrx instance without a net device. All data will be
+ * copied. The refill queue entries might not be automatically
+ * consumed and need to be flushed, see ZCRX_CTRL_FLUSH_RQ.
+ */
+ ZCRX_REG_NODEV = 2,
+};
+
+enum zcrx_features {
+ /*
+ * The user can ask for the desired rx page size by passing the
+ * value in struct io_uring_zcrx_ifq_reg::rx_buf_len.
+ */
+ ZCRX_FEATURE_RX_PAGE_SIZE = 1 << 0,
+};
+
/*
* Argument for IORING_REGISTER_ZCRX_IFQ
*/
@@ -1047,10 +1085,38 @@ struct io_uring_zcrx_ifq_reg {
struct io_uring_zcrx_offsets offsets;
__u32 zcrx_id;
- __u32 __resv2;
+ __u32 rx_buf_len;
__u64 __resv[3];
};
+enum zcrx_ctrl_op {
+ ZCRX_CTRL_FLUSH_RQ,
+ ZCRX_CTRL_EXPORT,
+
+ __ZCRX_CTRL_LAST,
+};
+
+struct zcrx_ctrl_flush_rq {
+ __u64 __resv[6];
+};
+
+struct zcrx_ctrl_export {
+ __u32 zcrx_fd;
+ __u32 __resv1[11];
+};
+
+struct zcrx_ctrl {
+ __u32 zcrx_id;
+ __u32 op; /* see enum zcrx_ctrl_op */
+ __u64 __resv[2];
+
+ union {
+ struct zcrx_ctrl_export zc_export;
+ struct zcrx_ctrl_flush_rq zc_flush;
+ };
+};
+
+
#ifdef __cplusplus
}
#endif
diff --git a/contrib/libs/liburing/src/include/liburing/io_uring/bpf_filter.h b/contrib/libs/liburing/src/include/liburing/io_uring/bpf_filter.h
new file mode 100644
index 00000000000..1b461d792a7
--- /dev/null
+++ b/contrib/libs/liburing/src/include/liburing/io_uring/bpf_filter.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT */
+/*
+ * Header file for the io_uring BPF filters.
+ */
+#ifndef LINUX_IO_URING_BPF_FILTER_H
+#define LINUX_IO_URING_BPF_FILTER_H
+
+#include <linux/types.h>
+
+/*
+ * Struct passed to filters.
+ */
+struct io_uring_bpf_ctx {
+ __u64 user_data;
+ __u8 opcode;
+ __u8 sqe_flags;
+ __u8 pdu_size; /* size of aux data for filter */
+ __u8 pad[5];
+ union {
+ struct {
+ __u32 family;
+ __u32 type;
+ __u32 protocol;
+ } socket;
+ struct {
+ __u64 flags;
+ __u64 mode;
+ __u64 resolve;
+ } open;
+ };
+};
+
+enum {
+ /*
+ * If set, any currently unset opcode will have a deny filter attached
+ */
+ IO_URING_BPF_FILTER_DENY_REST = 1,
+ /*
+ * If set, if kernel and application don't agree on pdu_size for
+ * the given opcode, fail the registration of the filter.
+ */
+ IO_URING_BPF_FILTER_SZ_STRICT = 2,
+};
+
+struct io_uring_bpf_filter {
+ __u32 opcode; /* io_uring opcode to filter */
+ __u32 flags;
+ __u32 filter_len; /* number of BPF instructions */
+ __u8 pdu_size; /* expected pdu size for opcode */
+ __u8 resv[3];
+ __u64 filter_ptr; /* pointer to BPF filter */
+ __u64 resv2[5];
+};
+
+enum {
+ IO_URING_BPF_CMD_FILTER = 1,
+};
+
+struct io_uring_bpf {
+ __u16 cmd_type; /* IO_URING_BPF_* values */
+ __u16 cmd_flags; /* none so far */
+ __u32 resv;
+ union {
+ struct io_uring_bpf_filter filter;
+ };
+};
+
+#endif
diff --git a/contrib/libs/liburing/src/include/liburing/io_uring/query.h b/contrib/libs/liburing/src/include/liburing/io_uring/query.h
index 5d754322a27..0b6248175e2 100644
--- a/contrib/libs/liburing/src/include/liburing/io_uring/query.h
+++ b/contrib/libs/liburing/src/include/liburing/io_uring/query.h
@@ -18,6 +18,8 @@ struct io_uring_query_hdr {
enum {
IO_URING_QUERY_OPCODES = 0,
+ IO_URING_QUERY_ZCRX = 1,
+ IO_URING_QUERY_SCQ = 2,
__IO_URING_QUERY_MAX,
};
@@ -36,6 +38,32 @@ struct io_uring_query_opcode {
__u64 enter_flags;
/* Bitmask of all supported IOSQE_* flags */
__u64 sqe_flags;
+ /* The number of available query opcodes */
+ __u32 nr_query_opcodes;
+ __u32 __pad;
+};
+
+struct io_uring_query_zcrx {
+ /* Bitmask of supported ZCRX_REG_* flags, */
+ __u64 register_flags;
+ /* Bitmask of all supported IORING_ZCRX_AREA_* flags */
+ __u64 area_flags;
+ /* The number of supported ZCRX_CTRL_* opcodes */
+ __u32 nr_ctrl_opcodes;
+ /* Bitmask of ZCRX_FEATURE_* indicating which features are available */
+ __u32 features;
+ /* The refill ring header size */
+ __u32 rq_hdr_size;
+ /* The alignment for the header */
+ __u32 rq_hdr_alignment;
+ __u64 __resv2;
+};
+
+struct io_uring_query_scq {
+ /* The SQ/CQ rings header size */
+ __u64 hdr_size;
+ /* The alignment for the header */
+ __u64 hdr_alignment;
};
#endif
diff --git a/contrib/libs/liburing/src/include/liburing/io_uring_version.h b/contrib/libs/liburing/src/include/liburing/io_uring_version.h
index 4b031be3820..e8c4828e385 100644
--- a/contrib/libs/liburing/src/include/liburing/io_uring_version.h
+++ b/contrib/libs/liburing/src/include/liburing/io_uring_version.h
@@ -3,6 +3,6 @@
#define LIBURING_VERSION_H
#define IO_URING_VERSION_MAJOR 2
-#define IO_URING_VERSION_MINOR 14
+#define IO_URING_VERSION_MINOR 15
#endif
diff --git a/contrib/libs/liburing/src/nolibc.c b/contrib/libs/liburing/src/nolibc.c
index 22d23ec50df..097e99ac027 100644
--- a/contrib/libs/liburing/src/nolibc.c
+++ b/contrib/libs/liburing/src/nolibc.c
@@ -34,13 +34,18 @@ struct uring_heap {
void *__uring_malloc(size_t len)
{
struct uring_heap *heap;
+ size_t total = sizeof(*heap) + len;
- heap = __sys_mmap(NULL, sizeof(*heap) + len, PROT_READ | PROT_WRITE,
+ /* check for overflow */
+ if (total < len)
+ return NULL;
+
+ heap = __sys_mmap(NULL, total, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (IS_ERR(heap))
return NULL;
- heap->len = sizeof(*heap) + len;
+ heap->len = total;
return heap->user_p;
}
diff --git a/contrib/libs/liburing/src/queue.c b/contrib/libs/liburing/src/queue.c
index 219a7fc20b7..4dcf8974209 100644
--- a/contrib/libs/liburing/src/queue.c
+++ b/contrib/libs/liburing/src/queue.c
@@ -102,10 +102,23 @@ static int _io_uring_get_cqe(struct io_uring *ring,
if (!need_enter)
break;
if (looped && data->has_ts) {
- struct io_uring_getevents_arg *arg = data->arg;
+ /*
+ * When IORING_ENTER_EXT_ARG_REG is set, data->arg
+ * carries a register-wait offset (an integer), not a
+ * pointer to io_uring_getevents_arg. Dereferencing it
+ * as a struct pointer causes a memory access violation.
+ * For the registered-wait path the kernel enforces the
+ * timeout, so treat any timeout the same as -ETIME here.
+ */
+ if (data->get_flags & IORING_ENTER_EXT_ARG_REG) {
+ if (!cqe && !err)
+ err = -ETIME;
+ } else {
+ struct io_uring_getevents_arg *arg = data->arg;
- if (!cqe && arg->ts && !err)
- err = -ETIME;
+ if (!cqe && arg->ts && !err)
+ err = -ETIME;
+ }
break;
}
@@ -157,24 +170,55 @@ static inline bool io_uring_peek_batch_cqe_(struct io_uring *ring,
unsigned *count)
{
unsigned ready = io_uring_cq_ready(ring);
- unsigned shift;
unsigned head;
unsigned mask;
unsigned last;
+ unsigned nr;
if (!ready)
return false;
- shift = io_uring_cqe_shift(ring);
head = *ring->cq.khead;
mask = ring->cq.ring_mask;
- if (ready < *count)
- *count = ready;
- last = head + *count;
- for (;head != last; head++)
- *(cqes++) = &ring->cq.cqes[(head & mask) << shift];
+ if (!(ring->flags & IORING_SETUP_CQE_MIXED)) {
+ unsigned shift = io_uring_cqe_shift(ring);
- return true;
+ if (ready < *count)
+ *count = ready;
+ last = head + *count;
+ for (;head != last; head++)
+ *(cqes++) = &ring->cq.cqes[(head & mask) << shift];
+
+ return true;
+ }
+
+ /*
+ * For mixed CQE rings, CQEs take up one or two slots, and the kernel
+ * may post skip entries to pad out the ring at wrap time. Only return
+ * pointers to real CQEs, with *count denoting the number of CQEs.
+ */
+ last = head + ready;
+ nr = 0;
+ while (head != last && nr < *count) {
+ struct io_uring_cqe *cqe = &ring->cq.cqes[head & mask];
+
+ if (cqe->flags & IORING_CQE_F_SKIP) {
+ /*
+ * A skip entry can only be consumed if it's at the
+ * current CQ head, stop the batch otherwise. It'll
+ * be at the head for the next peek.
+ */
+ if (nr)
+ break;
+ io_uring_cq_advance(ring, 1);
+ head++;
+ continue;
+ }
+ head += io_uring_cqe_nr(cqe);
+ cqes[nr++] = cqe;
+ }
+ *count = nr;
+ return nr != 0;
}
/*
@@ -332,6 +376,8 @@ int io_uring_wait_cqes_min_timeout(struct io_uring *ring,
struct __kernel_timespec *ts,
unsigned int min_wait_usec, sigset_t *sigmask)
{
+ if (!(ring->features & IORING_FEAT_MIN_TIMEOUT))
+ return -EINVAL;
return io_uring_wait_cqes_new(ring, cqe_ptr, wait_nr, ts, min_wait_usec,
sigmask);
}
diff --git a/contrib/libs/liburing/src/register.c b/contrib/libs/liburing/src/register.c
index b72f3213098..3f6ff6ed300 100644
--- a/contrib/libs/liburing/src/register.c
+++ b/contrib/libs/liburing/src/register.c
@@ -7,6 +7,7 @@
#include "liburing.h"
#include "setup.h"
#include "int_flags.h"
+#include "liburing/io_uring/bpf_filter.h"
#include "liburing/io_uring.h"
#include "liburing/sanitize.h"
@@ -334,7 +335,7 @@ int io_uring_close_ring_fd(struct io_uring *ring)
int io_uring_register_buf_ring(struct io_uring *ring,
struct io_uring_buf_reg *reg,
- unsigned int __maybe_unused flags)
+ unsigned int flags)
{
reg->flags |= flags;
return do_register(ring, IORING_REGISTER_PBUF_RING, reg, 1);
@@ -449,8 +450,15 @@ int io_uring_register_ifq(struct io_uring *ring,
return do_register(ring, IORING_REGISTER_ZCRX_IFQ, reg, 1);
}
+int io_uring_register_zcrx_ctrl(struct io_uring *ring, struct zcrx_ctrl *ctrl)
+{
+ return do_register(ring, IORING_REGISTER_ZCRX_CTRL, ctrl, 0);
+}
+
int io_uring_resize_rings(struct io_uring *ring, struct io_uring_params *p)
{
+ struct io_uring_sq sq;
+ struct io_uring_cq cq;
unsigned sq_head, sq_tail;
int ret;
@@ -464,16 +472,19 @@ int io_uring_resize_rings(struct io_uring *ring, struct io_uring_params *p)
if (ret < 0)
goto out;
+ memset(&sq, 0, sizeof(sq));
+ memset(&cq, 0, sizeof(cq));
+ ret = io_uring_mmap(ring->ring_fd, p, &sq, &cq);
+ if (ret)
+ goto out;
+
sq_head = ring->sq.sqe_head;
sq_tail = ring->sq.sqe_tail;
__sys_munmap(ring->sq.sqes, ring->sq.sqes_sz);
io_uring_unmap_rings(&ring->sq, &ring->cq);
- memset(&ring->sq, 0, sizeof(ring->sq));
- memset(&ring->cq, 0, sizeof(ring->cq));
- ret = io_uring_mmap(ring->ring_fd, p, &ring->sq, &ring->cq);
- if (ret)
- goto out;
+ ring->sq = sq;
+ ring->cq = cq;
ring->sq.sqe_head = sq_head;
ring->sq.sqe_tail = sq_tail;
@@ -493,8 +504,9 @@ out:
return ret;
}
-int io_uring_register_wait_reg(struct io_uring *ring,
- struct io_uring_reg_wait *reg, int nr)
+int io_uring_register_wait_reg(struct io_uring __maybe_unused *ring,
+ struct io_uring_reg_wait __maybe_unused *reg,
+ int __maybe_unused nr)
{
return -EINVAL;
}
@@ -515,3 +527,19 @@ int io_uring_set_iowait(struct io_uring *ring, bool enable_iowait)
ring->int_flags |= INT_FLAG_NO_IOWAIT;
return 0;
}
+
+int io_uring_register_bpf_filter(struct io_uring *ring,
+ struct io_uring_bpf *bpf)
+{
+ return do_register(ring, IORING_REGISTER_BPF_FILTER, bpf, 1);
+}
+
+int io_uring_register_bpf_filter_task(struct io_uring_bpf *bpf)
+{
+ return __sys_io_uring_register(-1, IORING_REGISTER_BPF_FILTER, bpf, 1);
+}
+
+int io_uring_register_query(struct io_uring_query_hdr *query)
+{
+ return __sys_io_uring_register(-1, IORING_REGISTER_QUERY, query, 0);
+}
diff --git a/contrib/libs/liburing/src/setup.c b/contrib/libs/liburing/src/setup.c
index 0a6a2fb31fc..3f5903b07cd 100644
--- a/contrib/libs/liburing/src/setup.c
+++ b/contrib/libs/liburing/src/setup.c
@@ -171,8 +171,16 @@ err:
__cold int io_uring_queue_mmap(int fd, struct io_uring_params *p,
struct io_uring *ring)
{
+ int ret;
+
memset(ring, 0, sizeof(*ring));
- return io_uring_mmap(fd, p, &ring->sq, &ring->cq);
+ ret = io_uring_mmap(fd, p, &ring->sq, &ring->cq);
+ if (!ret) {
+ ring->flags = p->flags;
+ ring->features = p->features;
+ ring->ring_fd = ring->enter_ring_fd = fd;
+ }
+ return ret;
}
static size_t io_uring_sqes_size(const struct io_uring *ring)
@@ -213,6 +221,13 @@ __cold int io_uring_ring_dontfork(struct io_uring *ring)
return 0;
}
+#ifndef MAP_HUGE_SHIFT
+#define MAP_HUGE_SHIFT 26
+#endif
+#ifndef MAP_HUGE_2MB
+#define MAP_HUGE_2MB (21U << MAP_HUGE_SHIFT)
+#endif
+
/* FIXME */
static size_t huge_page_size = 2 * 1024 * 1024;
@@ -237,12 +252,12 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
return ret;
sqes_mem = params_sqes_size(p, sq_entries);
- if (!(p->flags & IORING_SETUP_NO_SQARRAY))
- sqes_mem += sq_entries * sizeof(unsigned);
sqes_mem = (sqes_mem + page_size - 1) & ~(page_size - 1);
ring_mem = KRING_SIZE;
ring_mem += params_cq_size(p, cq_entries);
+ if (!(p->flags & IORING_SETUP_NO_SQARRAY))
+ ring_mem += sq_entries * sizeof(unsigned);
mem_used = sqes_mem + ring_mem;
mem_used = (mem_used + page_size - 1) & ~(page_size - 1);
@@ -266,7 +281,7 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
buf_size = page_size;
else {
buf_size = huge_page_size;
- map_hugetlb = MAP_HUGETLB;
+ map_hugetlb = MAP_HUGETLB | MAP_HUGE_2MB;
}
sqes_size = buf_size;
ptr = __sys_mmap(NULL, sqes_size, PROT_READ|PROT_WRITE,
@@ -289,7 +304,7 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
buf_size = page_size;
else {
buf_size = huge_page_size;
- map_hugetlb = MAP_HUGETLB;
+ map_hugetlb = MAP_HUGETLB | MAP_HUGE_2MB;
}
ptr = __sys_mmap(NULL, buf_size, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS|map_hugetlb,
@@ -519,10 +534,14 @@ static size_t rings_size(struct io_uring_params *p, unsigned entries,
* CQ ring size is number of pages that we need for the
* struct io_uring_cqe entries, which may be 16b (default) or
* 32b if the ring is setup with IORING_SETUP_CQE32. We also need
- * room for the head/tail parts.
+ * room for the head/tail parts, and the SQ array which the kernel
+ * places at the tail of the rings region, unless the ring is setup
+ * with IORING_SETUP_NO_SQARRAY.
*/
cq_size = params_cq_size(p, cq_entries);
cq_size += KRING_SIZE;
+ if (!(p->flags & IORING_SETUP_NO_SQARRAY))
+ cq_size += entries * sizeof(unsigned);
cq_size = (cq_size + page_size - 1) & ~(page_size - 1);
pages = (size_t) cq_size / page_size;
diff --git a/contrib/libs/liburing/test/232c93d07b74.t/ya.make b/contrib/libs/liburing/test/232c93d07b74.t/ya.make
index 6aa48cf3ddf..5b7ef4928ee 100644
--- a/contrib/libs/liburing/test/232c93d07b74.t/ya.make
+++ b/contrib/libs/liburing/test/232c93d07b74.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/35fa71a030ca.t/ya.make b/contrib/libs/liburing/test/35fa71a030ca.t/ya.make
index a9808471c95..ebd03f582ff 100644
--- a/contrib/libs/liburing/test/35fa71a030ca.t/ya.make
+++ b/contrib/libs/liburing/test/35fa71a030ca.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/500f9fbadef8.t/ya.make b/contrib/libs/liburing/test/500f9fbadef8.t/ya.make
index 4e444dae264..e9925c5ae39 100644
--- a/contrib/libs/liburing/test/500f9fbadef8.t/ya.make
+++ b/contrib/libs/liburing/test/500f9fbadef8.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/7ad0e4b2f83c.t/ya.make b/contrib/libs/liburing/test/7ad0e4b2f83c.t/ya.make
index c9b0bb970cf..d644a44182b 100644
--- a/contrib/libs/liburing/test/7ad0e4b2f83c.t/ya.make
+++ b/contrib/libs/liburing/test/7ad0e4b2f83c.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/8a9973408177.t/ya.make b/contrib/libs/liburing/test/8a9973408177.t/ya.make
index 1c6a8af0f7a..2ffaaecadb3 100644
--- a/contrib/libs/liburing/test/8a9973408177.t/ya.make
+++ b/contrib/libs/liburing/test/8a9973408177.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/917257daa0fe.t/ya.make b/contrib/libs/liburing/test/917257daa0fe.t/ya.make
index 0c428de8b0f..ca8b84352e3 100644
--- a/contrib/libs/liburing/test/917257daa0fe.t/ya.make
+++ b/contrib/libs/liburing/test/917257daa0fe.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/a0908ae19763.t/ya.make b/contrib/libs/liburing/test/a0908ae19763.t/ya.make
index baeb013b5be..090c36647e3 100644
--- a/contrib/libs/liburing/test/a0908ae19763.t/ya.make
+++ b/contrib/libs/liburing/test/a0908ae19763.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/a4c0b3decb33.t/ya.make b/contrib/libs/liburing/test/a4c0b3decb33.t/ya.make
index a83531ae7f9..ba37e755dd8 100644
--- a/contrib/libs/liburing/test/a4c0b3decb33.t/ya.make
+++ b/contrib/libs/liburing/test/a4c0b3decb33.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/accept-link.t/ya.make b/contrib/libs/liburing/test/accept-link.t/ya.make
index 4dc39bce734..99ed439b8de 100644
--- a/contrib/libs/liburing/test/accept-link.t/ya.make
+++ b/contrib/libs/liburing/test/accept-link.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/accept-mshot-stress.c b/contrib/libs/liburing/test/accept-mshot-stress.c
new file mode 100644
index 00000000000..aea0759a9ab
--- /dev/null
+++ b/contrib/libs/liburing/test/accept-mshot-stress.c
@@ -0,0 +1,514 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: stress test multishot accept under rapid connection churn.
+ * Submits a multishot accept and has client threads rapidly
+ * connecting and disconnecting, verifying all connections are
+ * properly accepted.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define NR_CONNS 200
+#define NR_BURST 32
+#define NR_BURST_ROUNDS 4
+#define NR_RECONNECTS 1000
+#define MSHOT_UDATA 1000
+#define SEND_BYTE 0xa5
+
+static int no_mshot_accept;
+
+struct stress_ctx {
+ pthread_barrier_t barrier;
+ struct sockaddr_in addr;
+ int nr_conns;
+};
+
+struct burst_ctx {
+ pthread_barrier_t barrier;
+ pthread_barrier_t round_barrier;
+ struct sockaddr_in addr;
+ int nr_burst;
+ int nr_rounds;
+};
+
+struct reconnect_ctx {
+ pthread_barrier_t barrier;
+ struct sockaddr_in addr;
+ int nr_reconnects;
+};
+
+static int create_listen_sock(struct sockaddr_in *addr)
+{
+ int fd, ret, val = 1;
+
+ fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = inet_addr("127.0.0.1");
+ ret = t_bind_ephemeral_port(fd, addr);
+ if (ret) {
+ close(fd);
+ return -1;
+ }
+
+ ret = listen(fd, 256);
+ if (ret < 0) {
+ perror("listen");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int arm_mshot_accept(struct io_uring *ring, int listen_fd)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe)
+ return -1;
+ io_uring_prep_multishot_accept(sqe, listen_fd, NULL, NULL, 0);
+ io_uring_sqe_set_data64(sqe, MSHOT_UDATA);
+ return io_uring_submit(ring);
+}
+
+/*
+ * Client thread for stress test: rapid connect + close
+ */
+static void *stress_client_fn(void *data)
+{
+ struct stress_ctx *ctx = data;
+ int i;
+
+ pthread_barrier_wait(&ctx->barrier);
+
+ for (i = 0; i < ctx->nr_conns; i++) {
+ int fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd < 0)
+ break;
+ if (connect(fd, (struct sockaddr *)&ctx->addr,
+ sizeof(ctx->addr)) < 0) {
+ close(fd);
+ break;
+ }
+ close(fd);
+ }
+
+ return NULL;
+}
+
+/*
+ * Stress test: multishot accept with NR_CONNS rapid connections
+ */
+static int test_accept_mshot_stress(void)
+{
+ struct io_uring ring;
+ struct stress_ctx ctx;
+ pthread_t thread;
+ int listen_fd, ret, accepted = 0;
+ struct __kernel_timespec ts = { .tv_sec = 5 };
+
+ ret = io_uring_queue_init(512, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ listen_fd = create_listen_sock(&ctx.addr);
+ if (listen_fd < 0) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ ctx.nr_conns = NR_CONNS;
+ pthread_barrier_init(&ctx.barrier, NULL, 2);
+
+ ret = arm_mshot_accept(&ring, listen_fd);
+ if (ret < 0) {
+ fprintf(stderr, "arm accept: %d\n", ret);
+ goto err;
+ }
+
+ pthread_create(&thread, NULL, stress_client_fn, &ctx);
+ pthread_barrier_wait(&ctx.barrier);
+
+ while (accepted < NR_CONNS) {
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+ if (ret == -ETIME)
+ break;
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto err_thread;
+ }
+
+ if (cqe->res == -EINVAL) {
+ no_mshot_accept = 1;
+ io_uring_cqe_seen(&ring, cqe);
+ pthread_join(thread, NULL);
+ close(listen_fd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "accept res: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_thread;
+ }
+
+ close(cqe->res);
+ accepted++;
+
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ io_uring_cqe_seen(&ring, cqe);
+ arm_mshot_accept(&ring, listen_fd);
+ continue;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ pthread_join(thread, NULL);
+
+ if (accepted < NR_CONNS) {
+ fprintf(stderr, "stress: accepted %d, expected %d\n",
+ accepted, NR_CONNS);
+ goto err;
+ }
+
+ close(listen_fd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+err_thread:
+ pthread_join(thread, NULL);
+err:
+ close(listen_fd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Client thread for burst test: open NR_BURST connections, wait for
+ * server to drain, close all, repeat.
+ */
+static void *burst_client_fn(void *data)
+{
+ struct burst_ctx *ctx = data;
+ int round;
+
+ pthread_barrier_wait(&ctx->barrier);
+
+ for (round = 0; round < ctx->nr_rounds; round++) {
+ int fds[NR_BURST];
+ int i;
+
+ for (i = 0; i < ctx->nr_burst; i++) {
+ fds[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fds[i] < 0)
+ break;
+ if (connect(fds[i], (struct sockaddr *)&ctx->addr,
+ sizeof(ctx->addr)) < 0) {
+ close(fds[i]);
+ fds[i] = -1;
+ break;
+ }
+ }
+
+ /* Signal server: all connections are open */
+ pthread_barrier_wait(&ctx->round_barrier);
+ /* Wait for server to drain */
+ pthread_barrier_wait(&ctx->round_barrier);
+
+ for (i = 0; i < ctx->nr_burst; i++) {
+ if (fds[i] >= 0)
+ close(fds[i]);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Burst test: client opens NR_BURST connections at once, server drains,
+ * client closes, repeat for NR_BURST_ROUNDS.
+ */
+static int test_accept_mshot_burst(void)
+{
+ struct io_uring ring;
+ struct burst_ctx ctx;
+ pthread_t thread;
+ int listen_fd, ret, total = 0;
+ struct __kernel_timespec ts = { .tv_sec = 5 };
+
+ if (no_mshot_accept)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(256, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ listen_fd = create_listen_sock(&ctx.addr);
+ if (listen_fd < 0) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ ctx.nr_burst = NR_BURST;
+ ctx.nr_rounds = NR_BURST_ROUNDS;
+ pthread_barrier_init(&ctx.barrier, NULL, 2);
+ pthread_barrier_init(&ctx.round_barrier, NULL, 2);
+
+ ret = arm_mshot_accept(&ring, listen_fd);
+ if (ret < 0)
+ goto err;
+
+ pthread_create(&thread, NULL, burst_client_fn, &ctx);
+ pthread_barrier_wait(&ctx.barrier);
+
+ for (int round = 0; round < NR_BURST_ROUNDS; round++) {
+ int round_accepted = 0;
+
+ /* Wait for client to open all connections */
+ pthread_barrier_wait(&ctx.round_barrier);
+
+ while (round_accepted < NR_BURST) {
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+ if (ret == -ETIME) {
+ fprintf(stderr, "burst round %d timeout at %d\n",
+ round, round_accepted);
+ goto err_thread;
+ }
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto err_thread;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "accept res: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_thread;
+ }
+
+ close(cqe->res);
+ round_accepted++;
+ total++;
+
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ io_uring_cqe_seen(&ring, cqe);
+ arm_mshot_accept(&ring, listen_fd);
+ continue;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ /* Signal client: done draining */
+ pthread_barrier_wait(&ctx.round_barrier);
+ }
+
+ pthread_join(thread, NULL);
+
+ if (total != NR_BURST * NR_BURST_ROUNDS) {
+ fprintf(stderr, "burst: accepted %d, expected %d\n",
+ total, NR_BURST * NR_BURST_ROUNDS);
+ goto err;
+ }
+
+ close(listen_fd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+err_thread:
+ pthread_join(thread, NULL);
+err:
+ close(listen_fd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Client thread for reconnect test: connect, send 1 byte, close, repeat.
+ */
+static void *reconnect_client_fn(void *data)
+{
+ struct reconnect_ctx *ctx = data;
+ unsigned char byte = SEND_BYTE;
+ int i;
+
+ pthread_barrier_wait(&ctx->barrier);
+
+ for (i = 0; i < ctx->nr_reconnects; i++) {
+ int fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd < 0)
+ break;
+ if (connect(fd, (struct sockaddr *)&ctx->addr,
+ sizeof(ctx->addr)) < 0) {
+ close(fd);
+ break;
+ }
+ send(fd, &byte, 1, 0);
+ close(fd);
+ }
+
+ return NULL;
+}
+
+/*
+ * Reconnect test: client connects, sends 1 byte, closes, reconnects.
+ * Server accepts each and reads the byte.
+ */
+static int test_accept_mshot_reconnect(void)
+{
+ struct io_uring ring;
+ struct reconnect_ctx ctx;
+ pthread_t thread;
+ int listen_fd, ret, accepted = 0;
+ struct __kernel_timespec ts = { .tv_sec = 5 };
+
+ if (no_mshot_accept)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(512, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ listen_fd = create_listen_sock(&ctx.addr);
+ if (listen_fd < 0) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ ctx.nr_reconnects = NR_RECONNECTS;
+ pthread_barrier_init(&ctx.barrier, NULL, 2);
+
+ ret = arm_mshot_accept(&ring, listen_fd);
+ if (ret < 0)
+ goto err;
+
+ pthread_create(&thread, NULL, reconnect_client_fn, &ctx);
+ pthread_barrier_wait(&ctx.barrier);
+
+ while (accepted < NR_RECONNECTS) {
+ struct io_uring_cqe *cqe;
+ unsigned char buf;
+ int conn_fd;
+
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+ if (ret == -ETIME)
+ break;
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto err_thread;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "accept res: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_thread;
+ }
+
+ conn_fd = cqe->res;
+
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ io_uring_cqe_seen(&ring, cqe);
+ arm_mshot_accept(&ring, listen_fd);
+ } else {
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ /* Read the byte the client sent */
+ ret = read(conn_fd, &buf, 1);
+ if (ret == 1 && buf != SEND_BYTE) {
+ fprintf(stderr, "bad byte: 0x%x\n", buf);
+ close(conn_fd);
+ goto err_thread;
+ }
+
+ close(conn_fd);
+ accepted++;
+ }
+
+ pthread_join(thread, NULL);
+
+ if (accepted < NR_RECONNECTS) {
+ fprintf(stderr, "reconnect: accepted %d, expected %d\n",
+ accepted, NR_RECONNECTS);
+ goto err;
+ }
+
+ close(listen_fd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+err_thread:
+ pthread_join(thread, NULL);
+err:
+ close(listen_fd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_accept_mshot_stress();
+ if (ret == T_EXIT_SKIP) {
+ printf("Multishot accept not supported, skipping\n");
+ return T_EXIT_SKIP;
+ }
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_accept_mshot_stress failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_accept_mshot_burst();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_accept_mshot_burst failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_accept_mshot_reconnect();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_accept_mshot_reconnect failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/accept-mshot-stress.t/ya.make b/contrib/libs/liburing/test/accept-mshot-stress.t/ya.make
new file mode 100644
index 00000000000..2feaa498f18
--- /dev/null
+++ b/contrib/libs/liburing/test/accept-mshot-stress.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ accept-mshot-stress.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/accept-non-empty.t/ya.make b/contrib/libs/liburing/test/accept-non-empty.t/ya.make
index 39f5ddabff8..7ff6d4962c8 100644
--- a/contrib/libs/liburing/test/accept-non-empty.t/ya.make
+++ b/contrib/libs/liburing/test/accept-non-empty.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/accept-reuse.t/ya.make b/contrib/libs/liburing/test/accept-reuse.t/ya.make
index 3bb1ccc90aa..5635663cc80 100644
--- a/contrib/libs/liburing/test/accept-reuse.t/ya.make
+++ b/contrib/libs/liburing/test/accept-reuse.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/accept-test.t/ya.make b/contrib/libs/liburing/test/accept-test.t/ya.make
index c498c33e409..9090a1ae641 100644
--- a/contrib/libs/liburing/test/accept-test.t/ya.make
+++ b/contrib/libs/liburing/test/accept-test.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/accept.t/ya.make b/contrib/libs/liburing/test/accept.t/ya.make
index 735da1cf17c..3c6c53e7e5d 100644
--- a/contrib/libs/liburing/test/accept.t/ya.make
+++ b/contrib/libs/liburing/test/accept.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/across-fork.t/ya.make b/contrib/libs/liburing/test/across-fork.t/ya.make
index 303548d9db8..7a5a742a3b2 100644
--- a/contrib/libs/liburing/test/across-fork.t/ya.make
+++ b/contrib/libs/liburing/test/across-fork.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/b19062a56726.t/ya.make b/contrib/libs/liburing/test/b19062a56726.t/ya.make
index 9e2670e4d35..927cd4be9a3 100644
--- a/contrib/libs/liburing/test/b19062a56726.t/ya.make
+++ b/contrib/libs/liburing/test/b19062a56726.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/b5837bd5311d.t/ya.make b/contrib/libs/liburing/test/b5837bd5311d.t/ya.make
index d47ef4283b3..c176c525f5f 100644
--- a/contrib/libs/liburing/test/b5837bd5311d.t/ya.make
+++ b/contrib/libs/liburing/test/b5837bd5311d.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/bind-listen.c b/contrib/libs/liburing/test/bind-listen.c
index 7ab73e39e4d..57512accf4d 100644
--- a/contrib/libs/liburing/test/bind-listen.c
+++ b/contrib/libs/liburing/test/bind-listen.c
@@ -270,7 +270,14 @@ static int test_good_server(unsigned int ring_flags)
sqe->flags |= IOSQE_FIXED_FILE;
io_uring_submit(&ring);
- io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+ if (ret) {
+ fprintf(stderr, "timeout waiting on recv: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
if (cqe->res < 0) {
fprintf(stderr, "bad receive cqe. %d\n", cqe->res);
diff --git a/contrib/libs/liburing/test/bind-listen.t/ya.make b/contrib/libs/liburing/test/bind-listen.t/ya.make
index 56fbf2d815f..b58f1f6f804 100644
--- a/contrib/libs/liburing/test/bind-listen.t/ya.make
+++ b/contrib/libs/liburing/test/bind-listen.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/buf-ring-mshot.c b/contrib/libs/liburing/test/buf-ring-mshot.c
new file mode 100644
index 00000000000..8ff3fc58439
--- /dev/null
+++ b/contrib/libs/liburing/test/buf-ring-mshot.c
@@ -0,0 +1,452 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: verify that a single buffer ring shared across multiple
+ * concurrent multishot recv operations works correctly.
+ * Tests buffer distribution, data integrity, and exhaustion
+ * handling with refill across multiple streams.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define NR_STREAMS 4
+#define BGID 1
+#define BUF_SIZE 256
+#define NR_BUFS_BASIC 32
+#define NR_BUFS_SMALL 8
+#define NR_SENDS 32
+#define NR_SENDS_EXHAUST 64
+#define NR_SENDS_BIG 128
+#define NR_SENDS_SMALL 8
+#define SEND_SIZE 128
+
+#define UDATA_STREAM(id) (((__u64)(id) + 1) << 32)
+#define UDATA_GET_STREAM(ud) ((int)(((ud) >> 32) - 1))
+
+struct stream {
+ int send_fd;
+ int recv_fd;
+ int nr_sends;
+ int bytes_sent;
+ int bytes_recv;
+};
+
+struct sender_ctx {
+ pthread_barrier_t *barrier;
+ struct stream *stream;
+ int stream_id;
+};
+
+struct test_ctx {
+ struct io_uring ring;
+ struct io_uring_buf_ring *br;
+ void *buf_base;
+ int nr_bufs;
+ struct stream streams[NR_STREAMS];
+};
+
+static int no_buf_ring;
+static int no_mshot_recv;
+
+static int setup_streams(struct test_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < NR_STREAMS; i++) {
+ int fds[2];
+ int ret;
+
+ ret = t_create_socket_pair(fds, true);
+ if (ret) {
+ fprintf(stderr, "socket pair %d: %d\n", i, ret);
+ /* cleanup already created pairs */
+ while (--i >= 0) {
+ close(ctx->streams[i].recv_fd);
+ close(ctx->streams[i].send_fd);
+ }
+ return -1;
+ }
+ ctx->streams[i].recv_fd = fds[0];
+ ctx->streams[i].send_fd = fds[1];
+ ctx->streams[i].bytes_sent = 0;
+ ctx->streams[i].bytes_recv = 0;
+ }
+
+ return 0;
+}
+
+static void close_streams(struct test_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < NR_STREAMS; i++) {
+ close(ctx->streams[i].recv_fd);
+ close(ctx->streams[i].send_fd);
+ }
+}
+
+static int setup_buf_ring(struct test_ctx *ctx, int nr_bufs)
+{
+ int ret, i;
+
+ ctx->nr_bufs = nr_bufs;
+ ctx->buf_base = malloc(nr_bufs * BUF_SIZE);
+ if (!ctx->buf_base)
+ return -ENOMEM;
+
+ ctx->br = io_uring_setup_buf_ring(&ctx->ring, nr_bufs, BGID, 0, &ret);
+ if (!ctx->br) {
+ free(ctx->buf_base);
+ ctx->buf_base = NULL;
+ if (ret == -EINVAL || ret == -ENOENT)
+ return ret;
+ fprintf(stderr, "buf ring setup: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < nr_bufs; i++) {
+ io_uring_buf_ring_add(ctx->br, ctx->buf_base + i * BUF_SIZE,
+ BUF_SIZE, i,
+ io_uring_buf_ring_mask(nr_bufs), i);
+ }
+ io_uring_buf_ring_advance(ctx->br, nr_bufs);
+
+ return 0;
+}
+
+static void cleanup_buf_ring(struct test_ctx *ctx)
+{
+ if (ctx->br) {
+ io_uring_free_buf_ring(&ctx->ring, ctx->br, ctx->nr_bufs, BGID);
+ ctx->br = NULL;
+ }
+ free(ctx->buf_base);
+ ctx->buf_base = NULL;
+}
+
+static void recycle_buffer(struct test_ctx *ctx, int bid)
+{
+ io_uring_buf_ring_add(ctx->br,
+ ctx->buf_base + bid * BUF_SIZE,
+ BUF_SIZE, bid,
+ io_uring_buf_ring_mask(ctx->nr_bufs), 0);
+ io_uring_buf_ring_advance(ctx->br, 1);
+}
+
+static void arm_recv(struct io_uring *ring, int fd, int stream_id)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_recv_multishot(sqe, fd, NULL, 0, 0);
+ sqe->buf_group = BGID;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ io_uring_sqe_set_data64(sqe, UDATA_STREAM(stream_id));
+}
+
+static void *sender_fn(void *data)
+{
+ struct sender_ctx *sc = data;
+ struct stream *s = sc->stream;
+ char buf[SEND_SIZE];
+ int i;
+
+ memset(buf, sc->stream_id & 0xff, sizeof(buf));
+
+ pthread_barrier_wait(sc->barrier);
+
+ for (i = 0; i < s->nr_sends; i++) {
+ int ret = send(s->send_fd, buf, sizeof(buf), 0);
+
+ if (ret < 0)
+ break;
+ s->bytes_sent += ret;
+ }
+
+ shutdown(s->send_fd, SHUT_WR);
+ return NULL;
+}
+
+/*
+ * Run a shared buffer ring test with the given parameters.
+ */
+static int run_shared_test(int nr_bufs, int *sends_per_stream)
+{
+ struct test_ctx ctx = { };
+ struct sender_ctx senders[NR_STREAMS];
+ pthread_t threads[NR_STREAMS];
+ pthread_barrier_t barrier;
+ struct __kernel_timespec ts = { .tv_sec = 5 };
+ int ret, i, done = 0;
+
+ ret = io_uring_queue_init(128, &ctx.ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = setup_streams(&ctx);
+ if (ret) {
+ io_uring_queue_exit(&ctx.ring);
+ return T_EXIT_FAIL;
+ }
+
+ ret = setup_buf_ring(&ctx, nr_bufs);
+ if (ret == -EINVAL || ret == -ENOENT) {
+ no_buf_ring = 1;
+ close_streams(&ctx);
+ io_uring_queue_exit(&ctx.ring);
+ return T_EXIT_SKIP;
+ }
+ if (ret) {
+ close_streams(&ctx);
+ io_uring_queue_exit(&ctx.ring);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < NR_STREAMS; i++)
+ ctx.streams[i].nr_sends = sends_per_stream[i];
+
+ pthread_barrier_init(&barrier, NULL, NR_STREAMS + 1);
+
+ for (i = 0; i < NR_STREAMS; i++) {
+ senders[i].barrier = &barrier;
+ senders[i].stream = &ctx.streams[i];
+ senders[i].stream_id = i;
+ pthread_create(&threads[i], NULL, sender_fn, &senders[i]);
+ }
+
+ /* Arm multishot recv on all streams */
+ for (i = 0; i < NR_STREAMS; i++)
+ arm_recv(&ctx.ring, ctx.streams[i].recv_fd, i);
+
+ ret = io_uring_submit(&ctx.ring);
+ if (ret != NR_STREAMS) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto err;
+ }
+
+ /* Release all senders */
+ pthread_barrier_wait(&barrier);
+
+ while (done < NR_STREAMS) {
+ struct io_uring_cqe *cqe;
+ int sid, bid;
+
+ ret = io_uring_wait_cqe_timeout(&ctx.ring, &cqe, &ts);
+ if (ret == -ETIME) {
+ fprintf(stderr, "timeout: %d streams done\n", done);
+ goto err;
+ }
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto err;
+ }
+
+ sid = UDATA_GET_STREAM(cqe->user_data);
+ if (sid < 0 || sid >= NR_STREAMS) {
+ fprintf(stderr, "bad user_data: 0x%llx\n",
+ (unsigned long long)cqe->user_data);
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ goto err;
+ }
+
+ /* Multishot recv not supported */
+ if (cqe->res == -EINVAL) {
+ no_mshot_recv = 1;
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ goto skip;
+ }
+
+ /* EOF */
+ if (cqe->res == 0) {
+ done++;
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ continue;
+ }
+
+ /* Buffer exhaustion */
+ if (cqe->res == -ENOBUFS) {
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ arm_recv(&ctx.ring, ctx.streams[sid].recv_fd, sid);
+ io_uring_submit(&ctx.ring);
+ continue;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "recv stream %d: %d\n", sid, cqe->res);
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ goto err;
+ }
+
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "no buffer flag stream %d\n", sid);
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ goto err;
+ }
+
+ bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
+ if (bid >= nr_bufs) {
+ fprintf(stderr, "bad bid %d (max %d)\n", bid, nr_bufs);
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ goto err;
+ }
+
+ /* Verify data integrity */
+ {
+ unsigned char *data = ctx.buf_base + bid * BUF_SIZE;
+ int j;
+
+ for (j = 0; j < cqe->res; j++) {
+ if (data[j] != (unsigned char)(sid & 0xff)) {
+ fprintf(stderr,
+ "data mismatch stream %d byte %d: "
+ "got 0x%x expected 0x%x\n",
+ sid, j, data[j],
+ (unsigned char)(sid & 0xff));
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ goto err;
+ }
+ }
+ }
+
+ ctx.streams[sid].bytes_recv += cqe->res;
+ recycle_buffer(&ctx, bid);
+
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ arm_recv(&ctx.ring, ctx.streams[sid].recv_fd, sid);
+ io_uring_submit(&ctx.ring);
+ continue;
+ }
+
+ io_uring_cqe_seen(&ctx.ring, cqe);
+ }
+
+ for (i = 0; i < NR_STREAMS; i++)
+ pthread_join(threads[i], NULL);
+
+ /* Verify all bytes received */
+ for (i = 0; i < NR_STREAMS; i++) {
+ if (ctx.streams[i].bytes_recv != ctx.streams[i].bytes_sent) {
+ fprintf(stderr,
+ "stream %d: recv %d bytes, sent %d bytes\n",
+ i, ctx.streams[i].bytes_recv,
+ ctx.streams[i].bytes_sent);
+ goto cleanup_fail;
+ }
+ }
+
+ cleanup_buf_ring(&ctx);
+ close_streams(&ctx);
+ io_uring_queue_exit(&ctx.ring);
+ return T_EXIT_PASS;
+
+skip:
+ for (i = 0; i < NR_STREAMS; i++)
+ pthread_join(threads[i], NULL);
+ cleanup_buf_ring(&ctx);
+ close_streams(&ctx);
+ io_uring_queue_exit(&ctx.ring);
+ return T_EXIT_SKIP;
+
+err:
+ for (i = 0; i < NR_STREAMS; i++)
+ pthread_join(threads[i], NULL);
+cleanup_fail:
+ cleanup_buf_ring(&ctx);
+ close_streams(&ctx);
+ io_uring_queue_exit(&ctx.ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Basic test: 4 streams sharing 32 buffers, moderate load
+ */
+static int test_shared_basic(void)
+{
+ int sends[NR_STREAMS];
+ int i;
+
+ for (i = 0; i < NR_STREAMS; i++)
+ sends[i] = NR_SENDS;
+
+ return run_shared_test(NR_BUFS_BASIC, sends);
+}
+
+/*
+ * Exhaustion test: 4 streams sharing only 8 buffers under heavy load
+ */
+static int test_shared_exhaust(void)
+{
+ int sends[NR_STREAMS];
+ int i;
+
+ if (no_buf_ring || no_mshot_recv)
+ return T_EXIT_SKIP;
+
+ for (i = 0; i < NR_STREAMS; i++)
+ sends[i] = NR_SENDS_EXHAUST;
+
+ return run_shared_test(NR_BUFS_SMALL, sends);
+}
+
+/*
+ * Uneven test: one heavy stream (128 sends), three light streams (8 sends)
+ */
+static int test_shared_uneven(void)
+{
+ int sends[NR_STREAMS];
+ int i;
+
+ if (no_buf_ring || no_mshot_recv)
+ return T_EXIT_SKIP;
+
+ sends[0] = NR_SENDS_BIG;
+ for (i = 1; i < NR_STREAMS; i++)
+ sends[i] = NR_SENDS_SMALL;
+
+ return run_shared_test(NR_BUFS_BASIC, sends);
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_shared_basic();
+ if (ret == T_EXIT_SKIP) {
+ printf("Buffer rings or multishot recv not supported, skipping\n");
+ return T_EXIT_SKIP;
+ }
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_shared_basic failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_shared_exhaust();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_shared_exhaust failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_shared_uneven();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_shared_uneven failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/buf-ring-mshot.t/ya.make b/contrib/libs/liburing/test/buf-ring-mshot.t/ya.make
new file mode 100644
index 00000000000..eae63e71f95
--- /dev/null
+++ b/contrib/libs/liburing/test/buf-ring-mshot.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ buf-ring-mshot.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/buf-ring-nommap.t/ya.make b/contrib/libs/liburing/test/buf-ring-nommap.t/ya.make
index 5f56e39a11c..406409316e4 100644
--- a/contrib/libs/liburing/test/buf-ring-nommap.t/ya.make
+++ b/contrib/libs/liburing/test/buf-ring-nommap.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/buf-ring-put.t/ya.make b/contrib/libs/liburing/test/buf-ring-put.t/ya.make
index d3e9aaa17c7..5ee6cde8ffd 100644
--- a/contrib/libs/liburing/test/buf-ring-put.t/ya.make
+++ b/contrib/libs/liburing/test/buf-ring-put.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/buf-ring-stress.c b/contrib/libs/liburing/test/buf-ring-stress.c
new file mode 100644
index 00000000000..347420c9f19
--- /dev/null
+++ b/contrib/libs/liburing/test/buf-ring-stress.c
@@ -0,0 +1,495 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: stress test for buffer ring drain and refill. Creates a
+ * small buffer ring, submits many reads that consume all
+ * buffers, verifies correct behavior when buffers are
+ * exhausted, then refills and continues. Tests both read
+ * and recv paths with buffer rings under pressure.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define BGID 1
+#define BUF_SIZE 256
+#define NR_BUFS 8
+#define NR_ROUNDS 10000
+#define FILE_SIZE (BUF_SIZE * NR_BUFS * NR_ROUNDS)
+
+static void provide_buffers(struct io_uring_buf_ring *br, void *base,
+ int nr_bufs, int buf_size)
+{
+ int i;
+
+ for (i = 0; i < nr_bufs; i++) {
+ void *addr = base + i * buf_size;
+ io_uring_buf_ring_add(br, addr, buf_size, i,
+ io_uring_buf_ring_mask(nr_bufs), i);
+ }
+ io_uring_buf_ring_advance(br, nr_bufs);
+}
+
+/*
+ * Test: submit reads consuming all buffers, refill, repeat.
+ * Verifies data integrity through the whole process.
+ */
+static int test_read_drain_refill(void)
+{
+ struct io_uring ring;
+ struct io_uring_buf_ring *br;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ void *buf_base;
+ char *file_data;
+ const char *fname = ".buf-ring-stress-tmp";
+ int fd, ret, round;
+ int total_read = 0;
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Setup buffer ring */
+ br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret);
+ if (!br) {
+ if (ret == -EINVAL || ret == -ENOENT) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "buf ring setup: %d\n", ret);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ buf_base = malloc(NR_BUFS * BUF_SIZE);
+ if (!buf_base)
+ goto err;
+
+ /* Create test file with known pattern */
+ file_data = malloc(FILE_SIZE);
+ if (!file_data)
+ goto err;
+
+ for (int i = 0; i < FILE_SIZE; i++)
+ file_data[i] = (char)(i & 0xff);
+
+ fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ if (fd < 0) {
+ perror("open write");
+ free(file_data);
+ goto err;
+ }
+ if (write(fd, file_data, FILE_SIZE) != FILE_SIZE) {
+ perror("write");
+ close(fd);
+ free(file_data);
+ goto err;
+ }
+ close(fd);
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ perror("open read");
+ free(file_data);
+ goto err;
+ }
+
+ for (round = 0; round < NR_ROUNDS; round++) {
+ int reads_submitted = 0;
+ int reads_completed = 0;
+
+ /* Provide fresh buffers for this round */
+ provide_buffers(br, buf_base, NR_BUFS, BUF_SIZE);
+
+ /* Submit NR_BUFS reads using buffer selection */
+ for (int i = 0; i < NR_BUFS; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe round %d/%d\n", round, i);
+ goto err_file;
+ }
+ io_uring_prep_read(sqe, fd, NULL, BUF_SIZE, total_read + i * BUF_SIZE);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+ sqe->user_data = (round << 16) | i;
+ reads_submitted++;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != reads_submitted) {
+ fprintf(stderr, "submit round %d: %d\n", round, ret);
+ goto err_file;
+ }
+
+ /* Reap all completions */
+ while (reads_completed < reads_submitted) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait round %d: %d\n", round, ret);
+ goto err_file;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "read error round %d: %d\n",
+ round, cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_file;
+ }
+
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "no buffer flag round %d\n", round);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_file;
+ }
+
+ int bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
+ if (bid >= NR_BUFS) {
+ fprintf(stderr, "bad bid %d round %d\n", bid, round);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_file;
+ }
+
+ /* Verify data integrity */
+ void *got = buf_base + bid * BUF_SIZE;
+ int idx = cqe->user_data & 0xffff;
+ int off = total_read + idx * BUF_SIZE;
+ if (cqe->res > 0 && memcmp(got, file_data + off, cqe->res) != 0) {
+ fprintf(stderr, "data mismatch round %d bid %d\n",
+ round, bid);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_file;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ reads_completed++;
+ }
+
+ total_read += NR_BUFS * BUF_SIZE;
+ }
+
+ if (total_read != FILE_SIZE) {
+ fprintf(stderr, "total read %d, expected %d\n", total_read, FILE_SIZE);
+ goto err_file;
+ }
+
+ close(fd);
+ unlink(fname);
+ free(file_data);
+ free(buf_base);
+ io_uring_free_buf_ring(&ring, br, NR_BUFS, BGID);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+err_file:
+ close(fd);
+ unlink(fname);
+ free(file_data);
+err:
+ free(buf_base);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Test: submit more reads than available buffers. Excess reads should
+ * fail with -ENOBUFS. Then refill and retry.
+ */
+static int test_read_overflow_refill(void)
+{
+ struct io_uring ring;
+ struct io_uring_buf_ring *br;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ void *buf_base;
+ const char *fname = ".buf-ring-overflow-tmp";
+ int fd, ret;
+ int nr_success = 0, nr_enobufs = 0;
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret);
+ if (!br) {
+ if (ret == -EINVAL || ret == -ENOENT) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "buf ring setup: %d\n", ret);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ buf_base = malloc(NR_BUFS * BUF_SIZE);
+ if (!buf_base)
+ goto err;
+
+ /* Create test file */
+ t_create_file(fname, NR_BUFS * BUF_SIZE * 4);
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ goto err;
+ }
+
+ /* Provide only NR_BUFS buffers */
+ provide_buffers(br, buf_base, NR_BUFS, BUF_SIZE);
+
+ /* Submit more reads than we have buffers */
+ for (int i = 0; i < NR_BUFS * 2; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fd, NULL, BUF_SIZE, i * BUF_SIZE);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+ sqe->user_data = i;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != NR_BUFS * 2) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto err_file;
+ }
+
+ /* Collect all completions */
+ for (int i = 0; i < NR_BUFS * 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto err_file;
+ }
+
+ if (cqe->res == -ENOBUFS) {
+ nr_enobufs++;
+ } else if (cqe->res > 0) {
+ nr_success++;
+ } else {
+ fprintf(stderr, "unexpected res %d for ud %llu\n",
+ cqe->res, (unsigned long long)cqe->user_data);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_file;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ /* We should have gotten exactly NR_BUFS successes */
+ if (nr_success != NR_BUFS) {
+ fprintf(stderr, "expected %d success, got %d\n", NR_BUFS, nr_success);
+ goto err_file;
+ }
+
+ if (nr_enobufs != NR_BUFS) {
+ fprintf(stderr, "expected %d ENOBUFS, got %d\n", NR_BUFS, nr_enobufs);
+ goto err_file;
+ }
+
+ /* Now refill and do another round - should work fine */
+ provide_buffers(br, buf_base, NR_BUFS, BUF_SIZE);
+
+ nr_success = 0;
+ for (int i = 0; i < NR_BUFS; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fd, NULL, BUF_SIZE, i * BUF_SIZE);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+ sqe->user_data = 100 + i;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != NR_BUFS) {
+ fprintf(stderr, "refill submit: %d\n", ret);
+ goto err_file;
+ }
+
+ for (int i = 0; i < NR_BUFS; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "refill wait: %d\n", ret);
+ goto err_file;
+ }
+
+ if (cqe->res <= 0) {
+ fprintf(stderr, "refill read res: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_file;
+ }
+ nr_success++;
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ if (nr_success != NR_BUFS) {
+ fprintf(stderr, "refill success: %d\n", nr_success);
+ goto err_file;
+ }
+
+ close(fd);
+ unlink(fname);
+ free(buf_base);
+ io_uring_free_buf_ring(&ring, br, NR_BUFS, BGID);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+err_file:
+ close(fd);
+ unlink(fname);
+err:
+ free(buf_base);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Test: rapid drain/refill cycles with concurrent reads
+ */
+static int test_rapid_drain_refill(void)
+{
+ struct io_uring ring;
+ struct io_uring_buf_ring *br;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ void *buf_base;
+ const char *fname = ".buf-ring-rapid-tmp";
+ int fd, ret;
+ int total_success = 0;
+
+ ret = io_uring_queue_init(64, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret);
+ if (!br) {
+ if (ret == -EINVAL || ret == -ENOENT) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "buf ring setup: %d\n", ret);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ buf_base = malloc(NR_BUFS * BUF_SIZE);
+ if (!buf_base)
+ goto err;
+
+ t_create_file(fname, FILE_SIZE);
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ goto err;
+ }
+
+ /*
+ * Do many rapid cycles: provide 1 buffer, submit 1 read,
+ * reap, repeat. This tests the fast path of single-buffer
+ * provide/consume cycles.
+ */
+ for (int i = 0; i < NR_ROUNDS * NR_BUFS; i++) {
+ /* Provide exactly 1 buffer */
+ void *addr = buf_base + (i % NR_BUFS) * BUF_SIZE;
+ io_uring_buf_ring_add(br, addr, BUF_SIZE, i % NR_BUFS,
+ io_uring_buf_ring_mask(NR_BUFS), 0);
+ io_uring_buf_ring_advance(br, 1);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fd, NULL, BUF_SIZE,
+ (i * BUF_SIZE) % FILE_SIZE);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+ sqe->user_data = i;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "rapid submit %d: %d\n", i, ret);
+ goto err_file;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "rapid wait %d: %d\n", i, ret);
+ goto err_file;
+ }
+
+ if (cqe->res <= 0) {
+ fprintf(stderr, "rapid read %d res: %d\n", i, cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_file;
+ }
+
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "rapid read %d: no buffer flag\n", i);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_file;
+ }
+
+ total_success++;
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ if (total_success != NR_ROUNDS * NR_BUFS) {
+ fprintf(stderr, "rapid: %d success, expected %d\n",
+ total_success, NR_ROUNDS * NR_BUFS);
+ goto err_file;
+ }
+
+ close(fd);
+ unlink(fname);
+ free(buf_base);
+ io_uring_free_buf_ring(&ring, br, NR_BUFS, BGID);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+err_file:
+ close(fd);
+ unlink(fname);
+err:
+ free(buf_base);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_read_drain_refill();
+ if (ret == T_EXIT_SKIP) {
+ printf("Buffer rings not supported, skipping\n");
+ return T_EXIT_SKIP;
+ }
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_read_drain_refill failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_read_overflow_refill();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_read_overflow_refill failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_rapid_drain_refill();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_rapid_drain_refill failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/buf-ring-stress.t/ya.make b/contrib/libs/liburing/test/buf-ring-stress.t/ya.make
new file mode 100644
index 00000000000..76fd09dffa5
--- /dev/null
+++ b/contrib/libs/liburing/test/buf-ring-stress.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ buf-ring-stress.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/buf-ring-upgrade.c b/contrib/libs/liburing/test/buf-ring-upgrade.c
new file mode 100644
index 00000000000..bfdf642403a
--- /dev/null
+++ b/contrib/libs/liburing/test/buf-ring-upgrade.c
@@ -0,0 +1,255 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test having a buffer group with legacy buffers, where those
+ * are consumed until the group is empty. Then when empty, the
+ * group is converted to a ring provided one, and finally the
+ * original buffer from the legacy group is recycled.
+ */
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define BGID 0x444
+#define BID0 0
+#define RING_ENTRIES 1024
+#define LEGACY_BUF_LEN 0x1000
+#define TRY_ITERS 800000
+#define POST_REG_ITERS 300000
+
+static volatile sig_atomic_t g_stop;
+
+struct shared_state {
+ volatile int stop;
+ volatile int reg_ok;
+ volatile int reg_err;
+ volatile int reg_no_br;
+};
+
+static void on_alarm(int sig)
+{
+ g_stop = 1;
+}
+
+static void pin_cpu(int cpu)
+{
+ cpu_set_t set;
+
+ CPU_ZERO(&set);
+ CPU_SET(cpu, &set);
+ sched_setaffinity(0, sizeof(set), &set);
+}
+
+static int sender_loop(int sfd, struct shared_state *sh)
+{
+ pin_cpu(1);
+
+ while (!sh->stop && !g_stop) {
+ ssize_t n = send(sfd, "", 0, MSG_DONTWAIT);
+
+ if (n < 0 && errno == EAGAIN)
+ sched_yield();
+ }
+ return 0;
+}
+
+static int reg_loop(int ring_fd, struct io_uring_buf_reg *reg,
+ struct shared_state *sh)
+{
+ pin_cpu(1);
+
+ while (!sh->stop && !g_stop) {
+ int ret;
+
+ ret = io_uring_register(ring_fd, IORING_REGISTER_PBUF_RING,
+ reg, 1);
+ if (ret == 0) {
+ sh->reg_ok = 1;
+ break;
+ }
+
+ if (ret == -EINVAL) {
+ sh->reg_no_br = 1;
+ break;
+ }
+ if (ret != -EEXIST && ret != -EBUSY && ret != -EINVAL)
+ sh->reg_err = -ret;
+ }
+
+ while (!sh->stop && !g_stop)
+ usleep(1000);
+
+ return 0;
+}
+
+static int run_poc(void)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring_buf_reg reg;
+ struct io_uring_buf_ring *br = NULL;
+ struct shared_state *sh = NULL;
+ char *legacy_buf = NULL;
+ char *ring_data = NULL;
+ size_t ring_bytes;
+ pid_t regger = -1, sender = -1;
+ int sv[2] = { -1, -1 }, ret, i, st, reg_seen_iter = -1;
+
+ pin_cpu(0);
+
+ sh = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (sh == MAP_FAILED) {
+ perror("mmap sh");
+ return T_EXIT_FAIL;
+ }
+ memset(sh, 0, 4096);
+
+ legacy_buf = mmap(NULL, LEGACY_BUF_LEN, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (legacy_buf == MAP_FAILED) {
+ perror("mmap legacy_buf");
+ return T_EXIT_FAIL;
+ }
+ memset(legacy_buf, 0x41, LEGACY_BUF_LEN);
+
+ ring_data = mmap(NULL, LEGACY_BUF_LEN, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (ring_data == MAP_FAILED) {
+ perror("mmap ring_data");
+ return T_EXIT_FAIL;
+ }
+ memset(ring_data, 0x42, LEGACY_BUF_LEN);
+
+ ring_bytes = (sizeof(struct io_uring_buf_ring) +
+ RING_ENTRIES * sizeof(struct io_uring_buf) + 4095) & ~4095UL;
+ br = mmap(NULL, ring_bytes, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (br == MAP_FAILED) {
+ perror("mmap br");
+ return T_EXIT_FAIL;
+ }
+
+ memset(br, 0, ring_bytes);
+ for (i = 0; i < RING_ENTRIES; i++) {
+ br->bufs[i].addr = (uint64_t)(uintptr_t)ring_data;
+ br->bufs[i].len = LEGACY_BUF_LEN;
+ br->bufs[i].bid = (uint16_t)i;
+ }
+ br->tail = RING_ENTRIES;
+
+ ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
+ if (ret < 0) {
+ perror("socketpair");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_queue_init(2, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "ring_init=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Provide a single legacy buffer for BGID */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_provide_buffers(sqe, legacy_buf, LEGACY_BUF_LEN, 1,
+ BGID, BID0);
+ ret = io_uring_submit_and_wait(&ring, 1);
+ if (ret < 0) {
+ fprintf(stderr, "submit_and_wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res < 0) {
+ io_uring_cqe_seen(&ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ memset(&reg, 0, sizeof(reg));
+ reg.ring_addr = (uint64_t)(uintptr_t)br;
+ reg.ring_entries = RING_ENTRIES;
+ reg.bgid = BGID;
+
+ sender = fork();
+ if (sender < 0)
+ return T_EXIT_FAIL;
+ if (sender == 0)
+ _exit(sender_loop(sv[1], sh));
+
+ regger = fork();
+ if (regger < 0)
+ return T_EXIT_FAIL;
+ if (regger == 0)
+ _exit(reg_loop(ring.ring_fd, &reg, sh));
+
+ for (i = 0; i < TRY_ITERS; i++) {
+ if (g_stop)
+ break;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_recv(sqe, sv[0], NULL, 1, 0);
+ sqe->flags = IOSQE_ASYNC | IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+
+ ret = io_uring_submit_and_wait(&ring, 1);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe %d\n", ret);
+ break;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe=%d at %u\n", ret, i);
+ break;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+
+ if (sh->reg_no_br)
+ break;
+ if (sh->reg_ok && reg_seen_iter < 0)
+ reg_seen_iter = (int)i;
+
+ if (reg_seen_iter >= 0 &&
+ (int)i - reg_seen_iter > POST_REG_ITERS)
+ break;
+ }
+
+ sh->stop = 1;
+ if (sender > 0)
+ waitpid(sender, &st, 0);
+ if (regger > 0)
+ waitpid(regger, &st, 0);
+
+ io_uring_queue_exit(&ring);
+ close(sv[0]);
+ close(sv[1]);
+ return T_EXIT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ signal(SIGALRM, on_alarm);
+ alarm(30);
+
+ return run_poc();
+}
diff --git a/contrib/libs/liburing/test/buf-ring-upgrade.t/ya.make b/contrib/libs/liburing/test/buf-ring-upgrade.t/ya.make
new file mode 100644
index 00000000000..ed0e2e0666c
--- /dev/null
+++ b/contrib/libs/liburing/test/buf-ring-upgrade.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ buf-ring-upgrade.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/buf-ring.t/ya.make b/contrib/libs/liburing/test/buf-ring.t/ya.make
index 87b8d4098ba..ddd44d240d5 100644
--- a/contrib/libs/liburing/test/buf-ring.t/ya.make
+++ b/contrib/libs/liburing/test/buf-ring.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/cancel-fd-userdata.c b/contrib/libs/liburing/test/cancel-fd-userdata.c
new file mode 100644
index 00000000000..955b09af27c
--- /dev/null
+++ b/contrib/libs/liburing/test/cancel-fd-userdata.c
@@ -0,0 +1,360 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test IORING_ASYNC_CANCEL_FD | IORING_ASYNC_CANCEL_USERDATA
+ *
+ * Tests that combining CANCEL_FD and CANCEL_USERDATA correctly matches
+ * requests by both file descriptor and user_data. These share a union
+ * in the kernel (io_cancel_data.data / io_cancel_data.file), so this
+ * exercises that the union is handled correctly.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int cancel_all(struct io_uring *ring, int fd, int fixed, int nr)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int i, ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
+ if (fixed)
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD_FIXED;
+ sqe->fd = fd;
+ sqe->user_data = 200;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1)
+ return 1;
+
+ for (i = 0; i < nr + 1; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret)
+ return 1;
+ io_uring_cqe_seen(ring, cqe);
+ }
+ return 0;
+}
+
+/*
+ * Submit multiple polls on the same fd with different user_data values.
+ * Cancel with CANCEL_FD | CANCEL_USERDATA targeting a specific user_data.
+ * Only the request matching both fd AND user_data should be canceled.
+ */
+static int test_cancel_fd_userdata(struct io_uring *ring, int *fd, int fixed)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i, __fd = fd[0];
+ int target_ud = 2;
+ int canceled = 0;
+
+ if (fixed) {
+ ret = io_uring_register_files(ring, fd, 2);
+ if (ret) {
+ fprintf(stderr, "file register: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ __fd = 0;
+ }
+
+ /* Submit 4 polls on the same fd, with user_data 1..4 */
+ for (i = 0; i < 4; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+ io_uring_prep_poll_add(sqe, __fd, POLLIN);
+ sqe->user_data = i + 1;
+ if (fixed)
+ sqe->flags |= IOSQE_FIXED_FILE;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != 4) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Cancel only the request with user_data == target_ud on this fd */
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_prep_cancel(sqe, 0, 0);
+ sqe->cancel_flags = IORING_ASYNC_CANCEL_FD |
+ IORING_ASYNC_CANCEL_USERDATA;
+ if (fixed)
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD_FIXED;
+ sqe->fd = __fd;
+ sqe->addr = target_ud;
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "cancel submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /*
+ * Get the cancel CQE first. If the kernel doesn't support these
+ * cancel flags, it will return -EINVAL and we skip the test.
+ */
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /*
+ * The cancel CQE may arrive before or after the canceled poll CQE.
+ * If we got the cancel result first, check it. If we got a poll
+ * completion first, consume it and get the cancel result next.
+ */
+ if (cqe->user_data == 100) {
+ if (cqe->res == -EINVAL) {
+ io_uring_cqe_seen(ring, cqe);
+ cancel_all(ring, __fd, fixed, 4);
+ if (fixed)
+ io_uring_unregister_files(ring);
+ return T_EXIT_SKIP;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "cancel failed: %d\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ /* Now get the canceled poll CQE */
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data != target_ud || cqe->res != -ECANCELED) {
+ fprintf(stderr, "unexpected ud=%lu res=%d\n",
+ (unsigned long)cqe->user_data, cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ canceled++;
+ } else if (cqe->user_data == target_ud) {
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll ud=%d res=%d\n",
+ target_ud, cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ canceled++;
+ io_uring_cqe_seen(ring, cqe);
+
+ /* Now get the cancel result CQE */
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data != 100 || cqe->res < 0) {
+ fprintf(stderr, "cancel ud=%lu res=%d\n",
+ (unsigned long)cqe->user_data, cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ } else {
+ fprintf(stderr, "unexpected user_data %lu res %d\n",
+ (unsigned long)cqe->user_data, cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+
+ if (canceled != 1) {
+ fprintf(stderr, "expected 1 canceled, got %d\n", canceled);
+ return T_EXIT_FAIL;
+ }
+
+ /* The other 3 polls should still be pending - cancel them */
+ if (cancel_all(ring, __fd, fixed, 3)) {
+ fprintf(stderr, "cleanup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (fixed)
+ io_uring_unregister_files(ring);
+
+ return T_EXIT_PASS;
+}
+
+/*
+ * Submit polls on two different fds with the same user_data.
+ * Cancel with CANCEL_FD | CANCEL_USERDATA should only match the
+ * request on the right fd with the right user_data.
+ */
+static int test_cancel_fd_userdata_two_fds(struct io_uring *ring, int *fd1,
+ int *fd2)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i;
+ int canceled = 0;
+
+ /* Poll on fd1 with user_data=1 */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_poll_add(sqe, fd1[0], POLLIN);
+ sqe->user_data = 1;
+
+ /* Poll on fd2 with user_data=1 (same user_data, different fd) */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_poll_add(sqe, fd2[0], POLLIN);
+ sqe->user_data = 1;
+
+ /* Poll on fd1 with user_data=2 (same fd, different user_data) */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_poll_add(sqe, fd1[0], POLLIN);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 3) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /*
+ * Cancel: fd=fd1[0], user_data=1. Should only cancel the first
+ * poll (fd1, ud=1), not the second (fd2, ud=1) or third (fd1, ud=2).
+ */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_cancel(sqe, 0, 0);
+ sqe->cancel_flags = IORING_ASYNC_CANCEL_FD |
+ IORING_ASYNC_CANCEL_USERDATA;
+ sqe->fd = fd1[0];
+ sqe->addr = 1;
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "cancel submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data == 100) {
+ if (cqe->res < 0) {
+ fprintf(stderr, "cancel res: %d\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ } else if (cqe->user_data == 1) {
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res: %d\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ canceled++;
+ } else {
+ fprintf(stderr, "unexpected ud=%lu res=%d\n",
+ (unsigned long)cqe->user_data, cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (canceled != 1) {
+ fprintf(stderr, "expected 1 cancel, got %d\n", canceled);
+ return T_EXIT_FAIL;
+ }
+
+ /* Clean up: cancel all remaining */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ANY |
+ IORING_ASYNC_CANCEL_ALL);
+ sqe->user_data = 200;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "cleanup submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "cleanup wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return T_EXIT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret, fd[2], fd2[2];
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ if (pipe(fd) < 0) {
+ perror("pipe");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_cancel_fd_userdata(&ring, fd, 0);
+ if (ret == T_EXIT_SKIP)
+ return T_EXIT_SKIP;
+ if (ret) {
+ fprintf(stderr, "test normal fd failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_cancel_fd_userdata(&ring, fd, 1);
+ if (ret == T_EXIT_SKIP)
+ return T_EXIT_SKIP;
+ if (ret) {
+ fprintf(stderr, "test fixed fd failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (pipe(fd2) < 0) {
+ perror("pipe");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_cancel_fd_userdata_two_fds(&ring, fd, fd2);
+ if (ret) {
+ fprintf(stderr, "test two fds failed\n");
+ return ret;
+ }
+
+ close(fd2[0]);
+ close(fd2[1]);
+ close(fd[0]);
+ close(fd[1]);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/cancel-fd-userdata.t/ya.make b/contrib/libs/liburing/test/cancel-fd-userdata.t/ya.make
new file mode 100644
index 00000000000..f884e79a22f
--- /dev/null
+++ b/contrib/libs/liburing/test/cancel-fd-userdata.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ cancel-fd-userdata.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/cancel-race.c b/contrib/libs/liburing/test/cancel-race.c
new file mode 100644
index 00000000000..5dc9ef67043
--- /dev/null
+++ b/contrib/libs/liburing/test/cancel-race.c
@@ -0,0 +1,437 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: stress test cancel racing with completion. Submits operations
+ * and immediately cancels them, checking that we get consistent
+ * results (either the op completed or was cancelled, never both
+ * or neither).
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define RING_SIZE 64
+#define NR_ITERS 10000
+
+/* user_data encoding: op type in low bits, sequence in upper bits */
+#define UD_OP_POLL 1
+#define UD_OP_CANCEL 2
+#define UD_OP_READ 3
+#define UD_SEQ_SHIFT 8
+
+static int no_cancel;
+
+/*
+ * Stress poll + cancel race: submit a poll and immediately cancel it.
+ * We should see exactly one completion per pair (either the poll
+ * completes or the cancel succeeds, or the cancel fails because the
+ * poll already completed).
+ */
+static int test_poll_cancel_race(void)
+{
+ struct io_uring ring;
+ int ret, i;
+ int pipe_fds[2];
+
+ if (pipe(pipe_fds) < 0) {
+ perror("pipe");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_queue_init(RING_SIZE, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < NR_ITERS; i++) {
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ __u64 poll_ud = UD_OP_POLL | ((__u64)i << UD_SEQ_SHIFT);
+ __u64 cancel_ud = UD_OP_CANCEL | ((__u64)i << UD_SEQ_SHIFT);
+ bool got_poll = false, got_cancel = false;
+
+ /* Submit poll */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, pipe_fds[0], POLLIN);
+ sqe->user_data = poll_ud;
+
+ /* Submit cancel immediately after */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_cancel64(sqe, poll_ud, 0);
+ sqe->user_data = cancel_ud;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "submit iter %d: %d\n", i, ret);
+ goto err;
+ }
+
+ /* Must get exactly 2 CQEs */
+ for (int j = 0; j < 2; j++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait iter %d/%d: %d\n", i, j, ret);
+ goto err;
+ }
+
+ if ((cqe->user_data & 0xff) == UD_OP_POLL) {
+ got_poll = true;
+ /*
+ * Poll result: either -ECANCELED (cancel won)
+ * or 0/POLLIN (poll completed first, shouldn't
+ * happen here since nothing writes to pipe)
+ */
+ if (cqe->res != -ECANCELED && cqe->res != 0) {
+ fprintf(stderr, "poll res: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+ } else if ((cqe->user_data & 0xff) == UD_OP_CANCEL) {
+ got_cancel = true;
+ /*
+ * Cancel result: 0 (cancelled successfully) or
+ * -ENOENT (poll already completed) or
+ * -EALREADY (in progress)
+ */
+ if (cqe->res != 0 && cqe->res != -ENOENT &&
+ cqe->res != -EALREADY) {
+ if (cqe->res == -EINVAL) {
+ no_cancel = 1;
+ io_uring_cqe_seen(&ring, cqe);
+ goto out;
+ }
+ fprintf(stderr, "cancel res: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ if (!got_poll || !got_cancel) {
+ fprintf(stderr, "missing CQE iter %d: poll=%d cancel=%d\n",
+ i, got_poll, got_cancel);
+ goto err;
+ }
+ }
+
+out:
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+
+ if (no_cancel)
+ return T_EXIT_SKIP;
+
+ return T_EXIT_PASS;
+
+err:
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Stress read + cancel race: submit a read on a pipe (that won't complete
+ * immediately) and immediately cancel it.
+ */
+static int test_read_cancel_race(void)
+{
+ struct io_uring ring;
+ char buf[32];
+ int ret, i;
+ int pipe_fds[2];
+
+ if (no_cancel)
+ return T_EXIT_SKIP;
+
+ if (pipe(pipe_fds) < 0) {
+ perror("pipe");
+ return T_EXIT_FAIL;
+ }
+
+ /* Make read end non-blocking so prep works, but reads will still
+ * go async since there's no data
+ */
+ t_set_nonblock(pipe_fds[0]);
+
+ ret = io_uring_queue_init(RING_SIZE, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < NR_ITERS; i++) {
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ __u64 read_ud = UD_OP_READ | ((__u64)i << UD_SEQ_SHIFT);
+ __u64 cancel_ud = UD_OP_CANCEL | ((__u64)i << UD_SEQ_SHIFT);
+ bool got_read = false, got_cancel = false;
+ int nr_cqes;
+
+ /* Submit read that will block (no data in pipe) */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, pipe_fds[0], buf, sizeof(buf), 0);
+ sqe->user_data = read_ud;
+
+ /* Submit cancel */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_cancel64(sqe, read_ud, 0);
+ sqe->user_data = cancel_ud;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "submit iter %d: %d\n", i, ret);
+ goto err;
+ }
+
+ /*
+ * We should get 2 CQEs, but the read may complete
+ * with -EAGAIN immediately (non-blocking pipe with no data),
+ * in which case cancel gets -ENOENT.
+ */
+ nr_cqes = 0;
+ while (nr_cqes < 2) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait iter %d/%d: %d\n",
+ i, nr_cqes, ret);
+ goto err;
+ }
+
+ if ((cqe->user_data & 0xff) == UD_OP_READ) {
+ got_read = true;
+ /* -ECANCELED, -EAGAIN, or short read all OK */
+ if (cqe->res != -ECANCELED &&
+ cqe->res != -EAGAIN &&
+ cqe->res < 0 && cqe->res != -EINTR) {
+ fprintf(stderr, "read res iter %d: %d\n",
+ i, cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+ } else if ((cqe->user_data & 0xff) == UD_OP_CANCEL) {
+ got_cancel = true;
+ if (cqe->res != 0 && cqe->res != -ENOENT &&
+ cqe->res != -EALREADY) {
+ fprintf(stderr, "cancel res iter %d: %d\n",
+ i, cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ nr_cqes++;
+ }
+
+ if (!got_read || !got_cancel) {
+ fprintf(stderr, "missing CQE iter %d\n", i);
+ goto err;
+ }
+ }
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+err:
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Stress concurrent cancel from multiple threads: one thread submits
+ * polls, another cancels them via IORING_ASYNC_CANCEL_ANY.
+ */
+struct cancel_thread_data {
+ struct io_uring *ring;
+ int cancel_count;
+ volatile int stop;
+};
+
+static void *cancel_thread_fn(void *arg)
+{
+ struct cancel_thread_data *ctd = arg;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ while (!ctd->stop) {
+ sqe = io_uring_get_sqe(ctd->ring);
+ if (!sqe) {
+ io_uring_submit(ctd->ring);
+ continue;
+ }
+ io_uring_prep_cancel64(sqe, 0, IORING_ASYNC_CANCEL_ANY);
+ sqe->user_data = 0xdead;
+
+ ret = io_uring_submit(ctd->ring);
+ if (ret < 0)
+ continue;
+
+ ret = io_uring_wait_cqe(ctd->ring, &cqe);
+ if (ret)
+ continue;
+
+ if (cqe->res == 0)
+ ctd->cancel_count++;
+ io_uring_cqe_seen(ctd->ring, cqe);
+ }
+
+ return NULL;
+}
+
+static int test_concurrent_cancel(void)
+{
+ struct io_uring submit_ring, cancel_ring;
+ struct cancel_thread_data ctd;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ pthread_t thread;
+ int ret, i;
+ int pipe_fds[2];
+ int submitted = 0, completed = 0;
+
+ if (no_cancel)
+ return T_EXIT_SKIP;
+
+ if (pipe(pipe_fds) < 0) {
+ perror("pipe");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_queue_init(RING_SIZE, &submit_ring, 0);
+ if (ret) {
+ fprintf(stderr, "submit ring setup: %d\n", ret);
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_queue_init(RING_SIZE, &cancel_ring, 0);
+ if (ret) {
+ fprintf(stderr, "cancel ring setup: %d\n", ret);
+ io_uring_queue_exit(&submit_ring);
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return T_EXIT_FAIL;
+ }
+
+ ctd.ring = &cancel_ring;
+ ctd.cancel_count = 0;
+ ctd.stop = 0;
+ pthread_create(&thread, NULL, cancel_thread_fn, &ctd);
+
+ /* Submit polls and let the other thread race cancels */
+ for (i = 0; i < NR_ITERS; i++) {
+ sqe = io_uring_get_sqe(&submit_ring);
+ if (!sqe) {
+ /* Flush and drain */
+ io_uring_submit(&submit_ring);
+ while (io_uring_peek_cqe(&submit_ring, &cqe) == 0) {
+ completed++;
+ io_uring_cqe_seen(&submit_ring, cqe);
+ }
+ sqe = io_uring_get_sqe(&submit_ring);
+ if (!sqe)
+ continue;
+ }
+ io_uring_prep_poll_add(sqe, pipe_fds[0], POLLIN);
+ sqe->user_data = i + 1;
+ submitted++;
+
+ if (i % 16 == 15) {
+ io_uring_submit(&submit_ring);
+ /* Drain any completions */
+ while (io_uring_peek_cqe(&submit_ring, &cqe) == 0) {
+ completed++;
+ io_uring_cqe_seen(&submit_ring, cqe);
+ }
+ }
+ }
+
+ io_uring_submit(&submit_ring);
+ ctd.stop = 1;
+ pthread_join(thread, NULL);
+
+ /* Drain remaining CQEs from submit ring */
+ while (completed < submitted) {
+ /* Cancel anything remaining */
+ sqe = io_uring_get_sqe(&submit_ring);
+ if (sqe) {
+ io_uring_prep_cancel64(sqe, 0, IORING_ASYNC_CANCEL_ANY);
+ sqe->user_data = 0xbeef;
+ io_uring_submit(&submit_ring);
+ }
+
+ ret = io_uring_peek_cqe(&submit_ring, &cqe);
+ if (ret == -EAGAIN) {
+ struct __kernel_timespec ts = { .tv_sec = 1 };
+ ret = io_uring_wait_cqe_timeout(&submit_ring, &cqe, &ts);
+ if (ret == -ETIME)
+ break;
+ if (ret)
+ break;
+ }
+ if (cqe->user_data != 0xbeef)
+ completed++;
+ io_uring_cqe_seen(&submit_ring, cqe);
+ }
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&cancel_ring);
+ io_uring_queue_exit(&submit_ring);
+ return T_EXIT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_poll_cancel_race();
+ if (ret == T_EXIT_SKIP) {
+ printf("cancel not supported, skipping\n");
+ return T_EXIT_SKIP;
+ }
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_poll_cancel_race failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_read_cancel_race();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_read_cancel_race failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_concurrent_cancel();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_concurrent_cancel failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/cancel-race.t/ya.make b/contrib/libs/liburing/test/cancel-race.t/ya.make
new file mode 100644
index 00000000000..e69e6f2dbfe
--- /dev/null
+++ b/contrib/libs/liburing/test/cancel-race.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ cancel-race.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/cbpf_filter.c b/contrib/libs/liburing/test/cbpf_filter.c
new file mode 100644
index 00000000000..ed09d2e1582
--- /dev/null
+++ b/contrib/libs/liburing/test/cbpf_filter.c
@@ -0,0 +1,2776 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test classic BPF (cBPF) filtering for io_uring operations.
+ *
+ * This test demonstrates using cBPF filters to restrict io_uring operations.
+ * Unlike eBPF which requires a separate compiled program, cBPF filters can
+ * be defined inline as an array of sock_filter instructions.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <linux/filter.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+#include "liburing.h"
+#include "liburing/io_uring/bpf_filter.h"
+#include "helpers.h"
+
+#ifndef RESOLVE_IN_ROOT
+#define RESOLVE_IN_ROOT 0x10
+#endif
+
+/*
+ * cBPF filter context layout (struct io_uring_bpf_ctx):
+ * offset 0: user_data (u64)
+ * offset 8: opcode (u8)
+ * offset 9: sqe_flags (u8)
+ * offset 10: pdu_size (u8)
+ * offset 11: pad[5]
+ * offset 16: union (socket: family/type/protocol at 16/20/24)
+ * (open: flags/mode/resolve at 16/24/32 - all u64)
+ */
+#define CTX_OFF_USER_DATA 0
+#define CTX_OFF_OPCODE 8
+#define CTX_OFF_SQE_FLAGS 9
+#define CTX_OFF_SOCKET_FAMILY 16
+#define CTX_OFF_SOCKET_TYPE 20
+#define CTX_OFF_SOCKET_PROTO 24
+#define CTX_OFF_OPEN_FLAGS 16 /* u64, use low 32 bits */
+#define CTX_OFF_OPEN_MODE 24 /* u64 */
+#define CTX_OFF_OPEN_RESOLVE 32 /* u64, use low 32 bits */
+/*
+ * connect: family @16 (u32), port @20 (__be16) + 2 pad,
+ * v4_addr @24 (__be32) / v6_addr @24 (u8[16]).
+ * pdu_size = 24 (one __u32 + one __be16 + 2 pad + 16 bytes).
+ * v6_addr is 16 bytes, accessed as four 4-byte words at offsets 24,
+ * 28, 32, 36 via BPF_LD|BPF_W|BPF_ABS.
+ */
+#define CTX_OFF_CONNECT_FAMILY 16
+#define CTX_OFF_CONNECT_PORT 20
+#define CTX_OFF_CONNECT_V4_ADDR 24
+#define CTX_OFF_CONNECT_V6_ADDR_W0 24 /* v6 bytes 0-3 */
+#define CTX_OFF_CONNECT_V6_ADDR_W1 28 /* v6 bytes 4-7 */
+#define CTX_OFF_CONNECT_V6_ADDR_W2 32 /* v6 bytes 8-11 */
+#define CTX_OFF_CONNECT_V6_ADDR_W3 36 /* v6 bytes 12-15 */
+#define CONNECT_PDU_SIZE 24
+
+/*
+ * Compile-time __be16 swap. htons() is a function call and is not
+ * usable in static initializers like BPF_JUMP K constants.
+ */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define CT_HTONS(x) ((__u16)(x))
+#else
+# define CT_HTONS(x) ((__u16)((((x) & 0xff) << 8) | (((x) >> 8) & 0xff)))
+#endif
+
+/*
+ * Compile-time K-constant for matching the __be16 port field via a
+ * BPF_LD|BPF_W|BPF_ABS load at CTX_OFF_CONNECT_PORT. The kernel
+ * populator writes port (__be16) at offset 20 with 2 zero pad bytes
+ * at offset 22-23, and bpf_prog_run reads in native host byte order.
+ * On LE the port lands in the low 16 bits; on BE the port lands in
+ * the high 16 bits. Pad bytes are guaranteed zero by the framework's
+ * memset, so no AND-mask is required.
+ */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define CT_PORT_K(p) ((__u32)(p) << 16)
+#else
+# define CT_PORT_K(p) ((__u32)CT_HTONS(p))
+#endif
+
+/*
+ * Compile-time K-constant for matching a 4-byte address slice (one v4
+ * address, one dword of a v6 address, or a /N subnet mask/base) via a
+ * BPF_LD|BPF_W|BPF_ABS load. Pass the bytes in their on-the-wire
+ * (network byte order) order; the macro emits the host-order u32 that
+ * the BPF interpreter will see after loading those bytes.
+ */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define CT_ADDR_K(a, b, c, d) \
+ (((__u32)(a) << 24) | ((__u32)(b) << 16) | ((__u32)(c) << 8) | (__u32)(d))
+#else
+# define CT_ADDR_K(a, b, c, d) \
+ (((__u32)(d) << 24) | ((__u32)(c) << 16) | ((__u32)(b) << 8) | (__u32)(a))
+#endif
+
+/*
+ * Simple cBPF filter that allows all operations.
+ * Returns 1 (non-zero) to allow.
+ */
+static struct sock_filter allow_all_filter[] = {
+ /* return 1 (allow) */
+ BPF_STMT(BPF_RET | BPF_K, 1),
+};
+
+/*
+ * Simple cBPF filter that denies all operations.
+ * Returns 0 to deny.
+ */
+static struct sock_filter deny_all_filter[] = {
+ /* return 0 (deny) */
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * cBPF filter that only allows AF_INET sockets (denies AF_INET6, etc).
+ * Checks the socket family field in the context.
+ */
+static struct sock_filter allow_inet_only_filter[] = {
+ /* Load socket family (32-bit at offset 16) */
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_SOCKET_FAMILY),
+ /* Jump if family == AF_INET (2), allow; else deny */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 1),
+ /* Allow: return 1 */
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ /* Deny: return 0 */
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * cBPF filter that only allows TCP sockets (SOCK_STREAM).
+ */
+static struct sock_filter allow_tcp_only_filter[] = {
+ /* Load socket type (32-bit at offset 20) */
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_SOCKET_TYPE),
+ /* Mask off SOCK_CLOEXEC/SOCK_NONBLOCK flags */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf),
+ /* Jump if type == SOCK_STREAM (1), allow; else deny */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SOCK_STREAM, 0, 1),
+ /* Allow: return 1 */
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ /* Deny: return 0 */
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * cBPF filter that denies O_CREAT flag for openat operations.
+ * Checks the flags field in the open context.
+ */
+static struct sock_filter deny_o_creat_filter[] = {
+ /* Load open flags (low 32 bits at offset 16) */
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_OPEN_FLAGS),
+ /* Check if O_CREAT bit is set */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, O_CREAT),
+ /* If result is non-zero (O_CREAT set), deny */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, 0),
+ /* Deny: return 0 */
+ BPF_STMT(BPF_RET | BPF_K, 0),
+ /* Allow: return 1 */
+ BPF_STMT(BPF_RET | BPF_K, 1),
+};
+
+/*
+ * cBPF filter that denies RESOLVE_IN_ROOT flag for openat2 operations.
+ * Checks the resolve field in the open context.
+ */
+static struct sock_filter deny_resolve_in_root_filter[] = {
+ /* Load resolve flags (low 32 bits at offset 32) */
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_OPEN_RESOLVE),
+ /* Check if RESOLVE_IN_ROOT bit is set */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, RESOLVE_IN_ROOT),
+ /* If result is non-zero (RESOLVE_IN_ROOT set), deny */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, 0),
+ /* Deny: return 0 */
+ BPF_STMT(BPF_RET | BPF_K, 0),
+ /* Allow: return 1 */
+ BPF_STMT(BPF_RET | BPF_K, 1),
+};
+
+/*
+ * cBPF filter that allows only AF_INET CONNECTs and denies everything
+ * else (a family-whitelist of AF_INET).
+ */
+static struct sock_filter connect_allow_family_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * cBPF filter that denies AF_UNIX CONNECTs and allows everything else
+ * (a family-blacklist of AF_UNIX).
+ */
+static struct sock_filter connect_deny_family_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_UNIX, 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * Deny AF_INET CONNECTs to 127.0.0.127 and allow the rest. The test
+ * address is byte-palindromic, so the K constant is endian-symmetric
+ * and CT_ADDR_K() is not needed here.
+ */
+static struct sock_filter connect_deny_v4_addr_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 2),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V4_ADDR),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x7f00007f, 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * Deny AF_INET CONNECTs to port 22 and allow the rest. Non-AF_INET
+ * traffic falls through to allow. Matches the port via CT_PORT_K().
+ */
+static struct sock_filter connect_deny_port_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_PORT),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_PORT_K(22), 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+};
+
+/*
+ * cBPF filter that denies AF_INET CONNECTs outright. Used by the
+ * stale-cache test: poisons the async msghdr with valid
+ * AF_INET state, then submits a short-len CONNECT and verifies the
+ * second one does NOT inherit AF_INET. When the framework zero-fill
+ * remains intact (the populator returns early via the addr_len
+ * guard), the filter sees family=0, falls through to allow, and the
+ * kernel net path returns -EINVAL for the short addr_len.
+ */
+static struct sock_filter connect_deny_inet_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+};
+
+/*
+ * cBPF filter that allows only AF_INET CONNECTs to 127.0.0.1 and
+ * denies everything else (a v4-address whitelist).
+ */
+static struct sock_filter connect_allow_v4_addr_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V4_ADDR),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_ADDR_K(127, 0, 0, 1), 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * Deny AF_INET6 CONNECTs to 2001:db8::dead and allow the rest.
+ * Walks the v6 address as four 4-byte word loads at offsets 24, 28,
+ * 32, 36.
+ */
+static struct sock_filter connect_deny_v6_addr_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET6, 0, 8),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W0),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_ADDR_K(0x20, 0x01, 0x0d, 0xb8), 0, 6),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W1),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 4),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W2),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 2),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W3),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_ADDR_K(0, 0, 0xde, 0xad), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * Allow only AF_INET6 CONNECTs to ::1 and deny everything else. Walks
+ * the v6 address as four 4-byte word loads at offsets 24, 28, 32, 36.
+ */
+static struct sock_filter connect_allow_v6_addr_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET6, 0, 9),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W0),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 7),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W1),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 5),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W2),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W3),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_ADDR_K(0, 0, 0, 1), 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * cBPF filter that allows only AF_INET CONNECTs to port 80 and denies
+ * everything else (a port whitelist).
+ */
+static struct sock_filter connect_allow_port_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_PORT),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_PORT_K(80), 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * Deny AF_INET CONNECTs in 127.42.0.0/24 and allow the rest. CIDR
+ * matching via load-mask-compare on the v4 address.
+ */
+static struct sock_filter connect_deny_v4_subnet_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V4_ADDR),
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, CT_ADDR_K(0xff, 0xff, 0xff, 0x00)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_ADDR_K(127, 42, 0, 0), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * cBPF filter that allows only AF_INET CONNECTs in the 127.0.0.0/24
+ * subnet and denies everything else (a v4 subnet whitelist).
+ */
+static struct sock_filter connect_allow_v4_subnet_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 4),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V4_ADDR),
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, CT_ADDR_K(0xff, 0xff, 0xff, 0x00)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_ADDR_K(127, 0, 0, 0), 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * cBPF filter that denies AF_INET6 CONNECTs in the 2001:db8::/32
+ * subnet and allows everything else. /32 falls on a word boundary, so
+ * an exact-match JEQ on the first v6 word suffices.
+ */
+static struct sock_filter connect_deny_v6_subnet_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET6, 0, 2),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W0),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_ADDR_K(0x20, 0x01, 0x0d, 0xb8), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/*
+ * cBPF filter that allows only AF_INET6 CONNECTs in the fe80::/16
+ * subnet (link-local) and denies everything else. /16 falls within
+ * the first v6 word, so we AND-mask the first 16 bits and compare.
+ */
+static struct sock_filter connect_allow_v6_subnet_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_FAMILY),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET6, 0, 4),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, CTX_OFF_CONNECT_V6_ADDR_W0),
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, CT_ADDR_K(0xff, 0xff, 0x00, 0x00)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, CT_ADDR_K(0xfe, 0x80, 0x00, 0x00), 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, 1),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+};
+
+/* Register a BPF filter on a task */
+static int register_bpf_filter(struct sock_filter *filter, unsigned int len,
+ __u32 opcode, __u8 pdu_size, int deny_rest)
+{
+ struct io_uring_bpf bpf = {
+ .cmd_type = IO_URING_BPF_CMD_FILTER,
+ .filter = {
+ .opcode = opcode,
+ .flags = deny_rest ? IO_URING_BPF_FILTER_DENY_REST : 0,
+ .filter_len = len,
+ .filter_ptr = (unsigned long) (uintptr_t) filter,
+ .pdu_size = pdu_size,
+ },
+ };
+
+ return io_uring_register_bpf_filter_task(&bpf);
+}
+
+/* Register a BPF filter on a ring */
+static int register_bpf_filter_ring(struct io_uring *ring,
+ struct sock_filter *filter, unsigned int len,
+ __u32 opcode, __u8 pdu_size, int deny_rest)
+{
+ struct io_uring_bpf bpf = {
+ .cmd_type = IO_URING_BPF_CMD_FILTER,
+ .filter = {
+ .opcode = opcode,
+ .flags = deny_rest ? IO_URING_BPF_FILTER_DENY_REST : 0,
+ .filter_len = len,
+ .filter_ptr = (unsigned long) (uintptr_t) filter,
+ .pdu_size = pdu_size,
+ },
+ };
+
+ return io_uring_register_bpf_filter(ring, &bpf);
+}
+
+/* Test NOP operation */
+static int test_nop(struct io_uring *ring, const char *desc, int should_succeed)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("FAIL (get_sqe)\n");
+ return -1;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 0x1234;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("FAIL (submit: %s)\n", strerror(-ret));
+ return ret;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("FAIL (wait: %s)\n", strerror(-ret));
+ return ret;
+ }
+
+ if (should_succeed) {
+ if (cqe->res >= 0) {
+ ret = 0;
+ } else {
+ printf("FAIL (expected success, got %s)\n",
+ strerror(-cqe->res));
+ ret = -1;
+ }
+ } else {
+ if (cqe->res == -EACCES) {
+ ret = 0;
+ } else {
+ printf("FAIL (expected -EACCES, got %d)\n", cqe->res);
+ ret = -1;
+ }
+ }
+
+ if (ret)
+ fprintf(stderr, "%s: %s: failed\n", __FUNCTION__, desc);
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+/* Test socket operation */
+static int test_socket(struct io_uring *ring, int family, int type,
+ const char *desc, int should_succeed)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_socket(sqe, family, type, 0, 0);
+ sqe->user_data = 0x5678;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("FAIL (submit: %s)\n", strerror(-ret));
+ return ret;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("FAIL (wait: %s)\n", strerror(-ret));
+ return ret;
+ }
+
+ if (should_succeed) {
+ if (cqe->res >= 0) {
+ close(cqe->res);
+ ret = 0;
+ } else {
+ printf("FAIL (expected success, got %s)\n",
+ strerror(-cqe->res));
+ ret = -1;
+ }
+ } else {
+ if (cqe->res == -EACCES) {
+ ret = 0;
+ } else if (cqe->res < 0) {
+ printf("FAIL (expected -EACCES, got %s)\n",
+ strerror(-cqe->res));
+ ret = -1;
+ } else {
+ printf("FAIL (expected denial, got fd=%d)\n", cqe->res);
+ close(cqe->res);
+ ret = -1;
+ }
+ }
+
+ if (ret)
+ fprintf(stderr, "%s: %s: failed\n", __FUNCTION__, desc);
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+/* Test openat operation */
+static int test_openat(struct io_uring *ring, const char *path, int flags,
+ mode_t mode, const char *desc, int should_succeed)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_openat(sqe, AT_FDCWD, path, flags, mode);
+ sqe->user_data = 0xabcd;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("FAIL (submit: %s)\n", strerror(-ret));
+ return ret;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("FAIL (wait: %s)\n", strerror(-ret));
+ return ret;
+ }
+
+ if (should_succeed) {
+ if (cqe->res >= 0) {
+ close(cqe->res);
+ ret = 0;
+ } else {
+ printf("FAIL (expected success, got %s)\n",
+ strerror(-cqe->res));
+ ret = -1;
+ }
+ } else {
+ if (cqe->res == -EACCES) {
+ ret = 0;
+ } else if (cqe->res < 0) {
+ printf("FAIL (expected -EACCES, got %s)\n",
+ strerror(-cqe->res));
+ ret = -1;
+ } else {
+ printf("FAIL (expected denial, got fd=%d)\n", cqe->res);
+ close(cqe->res);
+ ret = -1;
+ }
+ }
+
+ if (ret)
+ fprintf(stderr, "%s: %s: failed\n", __FUNCTION__, desc);
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+/* Test openat2 operation */
+static int test_openat2(struct io_uring *ring, const char *path,
+ struct open_how *how, const char *desc,
+ int should_succeed)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_openat2(sqe, AT_FDCWD, path, how);
+ sqe->user_data = 0xef01;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("FAIL (submit: %s)\n", strerror(-ret));
+ return ret;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("FAIL (wait: %s)\n", strerror(-ret));
+ return ret;
+ }
+
+ if (should_succeed) {
+ if (cqe->res >= 0) {
+ close(cqe->res);
+ ret = 0;
+ } else {
+ printf("FAIL (expected success, got %s)\n",
+ strerror(-cqe->res));
+ ret = -1;
+ }
+ } else {
+ if (cqe->res == -EACCES) {
+ ret = 0;
+ } else if (cqe->res < 0) {
+ printf("FAIL (expected -EACCES, got %s)\n",
+ strerror(-cqe->res));
+ ret = -1;
+ } else {
+ printf("FAIL (expected denial, got fd=%d)\n", cqe->res);
+ close(cqe->res);
+ ret = -1;
+ }
+ }
+
+ if (ret)
+ fprintf(stderr, "%s: %s: failed\n", __FUNCTION__, desc);
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+/*
+ * Submit an IORING_OP_CONNECT to @sa/@slen. should_succeed == 1 means
+ * the filter must allow the op through (cqe->res != -EACCES); the
+ * connect itself may still fail, typically with -ECONNREFUSED on
+ * closed loopback ports. Any non--EACCES result means the kernel net
+ * path ran. should_succeed == 0 means the filter must deny
+ * (cqe->res == -EACCES). The socket fd is consumed.
+ */
+static int test_connect(struct io_uring *ring, const struct sockaddr *sa,
+ socklen_t slen, const char *desc, int should_succeed)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int fd, ret;
+
+ fd = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (fd < 0) {
+ printf("FAIL (socket: %s)\n", strerror(errno));
+ return -1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_connect(sqe, fd, sa, slen);
+ sqe->user_data = 0x9abc;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("FAIL (submit: %s)\n", strerror(-ret));
+ close(fd);
+ return ret;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("FAIL (wait: %s)\n", strerror(-ret));
+ close(fd);
+ return ret;
+ }
+
+ ret = 0;
+ if (should_succeed && cqe->res == -EACCES) {
+ printf("FAIL (expected allow, got -EACCES)\n");
+ ret = -1;
+ } else if (!should_succeed && cqe->res != -EACCES) {
+ printf("FAIL (expected -EACCES, got %s)\n",
+ strerror(cqe->res < 0 ? -cqe->res : 0));
+ ret = -1;
+ }
+ if (ret)
+ fprintf(stderr, "%s: %s: failed\n", __FUNCTION__, desc);
+ io_uring_cqe_seen(ring, cqe);
+ close(fd);
+ return ret;
+}
+
+static int test_deny_nop(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ /* Fork to get fresh task restrictions */
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* Child process */
+ ret = register_bpf_filter(deny_all_filter,
+ sizeof(deny_all_filter) / sizeof(deny_all_filter[0]),
+ IORING_OP_NOP, 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed\n");
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_nop(&ring, "NOP should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ /* Parent waits for child */
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_allow_inet_only(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ /* Fork to get fresh task restrictions */
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* Child process */
+ ret = register_bpf_filter(allow_inet_only_filter,
+ sizeof(allow_inet_only_filter) / sizeof(allow_inet_only_filter[0]),
+ IORING_OP_SOCKET, 12, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed\n");
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_socket(&ring, AF_INET, SOCK_STREAM,
+ "AF_INET TCP should succeed", 1) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_INET6, SOCK_STREAM,
+ "AF_INET6 TCP should be denied", 0) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_UNIX, SOCK_STREAM,
+ "AF_UNIX should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ /* Parent waits for child */
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_allow_tcp_only(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ ret = register_bpf_filter(allow_tcp_only_filter,
+ sizeof(allow_tcp_only_filter) / sizeof(allow_tcp_only_filter[0]),
+ IORING_OP_SOCKET, 12, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed\n");
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_socket(&ring, AF_INET, SOCK_STREAM,
+ "TCP should succeed", 1) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_INET, SOCK_DGRAM,
+ "UDP should be denied", 0) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_INET6, SOCK_STREAM,
+ "IPv6 TCP should succeed", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_deny_rest(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* Register allow filter for NOP with DENY_REST flag */
+ ret = register_bpf_filter(allow_all_filter,
+ sizeof(allow_all_filter) / sizeof(allow_all_filter[0]),
+ IORING_OP_NOP, 0,
+ 1); /* deny_rest = true */
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed\n");
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_nop(&ring, "NOP should succeed", 1) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_INET, SOCK_STREAM,
+ "Socket should be denied (DENY_REST)", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Test denying O_CREAT flag for IORING_OP_OPENAT.
+ * Verifies the operation works before filter installation,
+ * then fails with -EACCES after.
+ */
+static int test_deny_openat_creat(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+ char tmpfile[] = "/tmp/cbpf_test_XXXXXX";
+ int tmpfd;
+
+ /* Create a temp file path we can use for testing */
+ tmpfd = mkstemp(tmpfile);
+ if (tmpfd < 0) {
+ perror("mkstemp");
+ return 1;
+ }
+ close(tmpfd);
+ unlink(tmpfile);
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* Test that O_CREAT works BEFORE installing filter */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_openat(&ring, tmpfile, O_CREAT | O_RDWR, 0644,
+ "O_CREAT should succeed before filter", 1) != 0)
+ failed++;
+
+ /* Clean up created file */
+ unlink(tmpfile);
+
+ /* Test that regular open (no O_CREAT) works */
+ if (test_openat(&ring, "/dev/null", O_RDONLY, 0,
+ "regular open should succeed before filter", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+
+ /* Now install the O_CREAT deny filter */
+ ret = register_bpf_filter(deny_o_creat_filter,
+ sizeof(deny_o_creat_filter) / sizeof(deny_o_creat_filter[0]),
+ IORING_OP_OPENAT, 24, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ /* Create new ring after filter is installed */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init 2 failed\n");
+ exit(1);
+ }
+
+ /* Test that O_CREAT is now denied */
+ if (test_openat(&ring, tmpfile, O_CREAT | O_RDWR, 0644,
+ "O_CREAT should be denied after filter", 0) != 0)
+ failed++;
+
+ /* Test that regular open still works */
+ if (test_openat(&ring, "/dev/null", O_RDONLY, 0,
+ "regular open should still succeed", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Test denying RESOLVE_IN_ROOT flag for IORING_OP_OPENAT2.
+ * Verifies the operation works before filter installation,
+ * then fails with -EACCES after.
+ *
+ * Note: RESOLVE_IN_ROOT requires a relative path since it treats dfd as root.
+ * We use "." with O_DIRECTORY to test this.
+ */
+static int test_deny_openat2_resolve_in_root(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+ struct open_how how_with_resolve = {
+ .flags = O_RDONLY | O_DIRECTORY,
+ .mode = 0,
+ .resolve = RESOLVE_IN_ROOT,
+ };
+ struct open_how how_normal = {
+ .flags = O_RDONLY | O_DIRECTORY,
+ .mode = 0,
+ .resolve = 0,
+ };
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* Test that RESOLVE_IN_ROOT works BEFORE installing filter */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_openat2(&ring, ".", &how_with_resolve,
+ "RESOLVE_IN_ROOT should succeed before filter", 1) != 0)
+ failed++;
+
+ /* Test that normal openat2 works */
+ if (test_openat2(&ring, ".", &how_normal,
+ "normal openat2 should succeed before filter", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+
+ /* Now install the RESOLVE_IN_ROOT deny filter */
+ ret = register_bpf_filter(deny_resolve_in_root_filter,
+ sizeof(deny_resolve_in_root_filter) / sizeof(deny_resolve_in_root_filter[0]),
+ IORING_OP_OPENAT2, 24, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ /* Create new ring after filter is installed */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init 2 failed\n");
+ exit(1);
+ }
+
+ /* Test that RESOLVE_IN_ROOT is now denied */
+ if (test_openat2(&ring, ".", &how_with_resolve,
+ "RESOLVE_IN_ROOT should be denied after filter", 0) != 0)
+ failed++;
+
+ /* Test that normal openat2 still works */
+ if (test_openat2(&ring, ".", &how_normal,
+ "normal openat2 should still succeed", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_allow_family(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in v4 = {
+ .sin_family = AF_INET,
+ .sin_port = htons(1),
+ };
+ struct sockaddr_in6 v6 = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(1),
+ };
+ struct sockaddr_un un = { .sun_family = AF_UNIX };
+
+ v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ v6.sin6_addr = in6addr_loopback;
+ strncpy(un.sun_path, "/tmp/cbpf_filter_no_such_socket",
+ sizeof(un.sun_path) - 1);
+
+ ret = register_bpf_filter(connect_allow_family_filter,
+ sizeof(connect_allow_family_filter) / sizeof(connect_allow_family_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&v4, sizeof(v4),
+ "AF_INET should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&v6, sizeof(v6),
+ "AF_INET6 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&un, sizeof(un),
+ "AF_UNIX should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_deny_v4_addr(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in banned = {
+ .sin_family = AF_INET,
+ .sin_port = htons(1),
+ };
+ struct sockaddr_in other = {
+ .sin_family = AF_INET,
+ .sin_port = htons(1),
+ };
+
+ banned.sin_addr.s_addr = htonl(0x7f00007f);
+ other.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ ret = register_bpf_filter(connect_deny_v4_addr_filter,
+ sizeof(connect_deny_v4_addr_filter) / sizeof(connect_deny_v4_addr_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&banned, sizeof(banned),
+ "127.0.0.127 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&other, sizeof(other),
+ "127.0.0.1 should be allowed", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_deny_port(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in ssh = {
+ .sin_family = AF_INET,
+ .sin_port = htons(22),
+ };
+ struct sockaddr_in http = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+
+ ssh.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ http.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ ret = register_bpf_filter(connect_deny_port_filter,
+ sizeof(connect_deny_port_filter) / sizeof(connect_deny_port_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&ssh, sizeof(ssh),
+ "port 22 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&http, sizeof(http),
+ "port 80 should be allowed", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Test for io_connect_bpf_populate's addr_len handling.
+ * Two kernel-side mechanisms cooperate: the framework's caller-side
+ * memset in io_uring_populate_bpf_ctx() zero-fills bctx before the
+ * populator runs, and the populator returns early when addr_len does
+ * not cover the family discriminator (sizeof(sa_family_t)) so the
+ * zero-fill stays intact. Step 1 poisons iomsg->addr with a denied
+ * AF_INET CONNECT. Step 2 submits CONNECT with addr_len=1: the
+ * filter must see family=0 and fall through to the kernel net path,
+ * which returns -EINVAL for the sub-minimum addr_len. If the
+ * populator read the stale AF_INET cache instead, the filter would
+ * deny with -EACCES -- the failure mode this test catches.
+ */
+static int test_connect_stale_addr_len(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in sa = {
+ .sin_family = AF_INET,
+ .sin_port = htons(1),
+ };
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int fd;
+
+ sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ ret = register_bpf_filter(connect_deny_inet_filter,
+ sizeof(connect_deny_inet_filter) / sizeof(connect_deny_inet_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ /*
+ * Step 1: poison iomsg->addr by submitting a fully-formed
+ * AF_INET CONNECT. The submit path's move_addr_to_kernel()
+ * copies the user sockaddr into the async msghdr before the
+ * filter runs; the filter then denies based on the populated
+ * family, leaving the AF_INET state cached in iomsg->addr.
+ */
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("stale: socket step1");
+ exit(1);
+ }
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "stale: get_sqe step1 failed\n");
+ close(fd);
+ exit(1);
+ }
+ io_uring_prep_connect(sqe, fd, (struct sockaddr *)&sa,
+ sizeof(sa));
+ ret = io_uring_submit(&ring);
+ if (ret < 0) {
+ fprintf(stderr, "stale: submit step1: %s\n",
+ strerror(-ret));
+ close(fd);
+ exit(1);
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "stale: wait step1: %s\n",
+ strerror(-ret));
+ close(fd);
+ exit(1);
+ }
+ if (cqe->res != -EACCES) {
+ fprintf(stderr, "stale: poison expected -EACCES, got %d\n",
+ cqe->res);
+ failed++;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ close(fd);
+
+ /*
+ * Step 2: short-len CONNECT. Without the guard, this would
+ * reuse stale AF_INET from step 1 and be denied with
+ * -EACCES. With the guard, the filter sees family=0, allows
+ * the op through, and the kernel net path rejects the
+ * sub-minimum addr_len with -EINVAL -- which is the
+ * specific result we assert.
+ */
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("stale: socket step2");
+ exit(1);
+ }
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "stale: get_sqe step2 failed\n");
+ close(fd);
+ exit(1);
+ }
+ io_uring_prep_connect(sqe, fd, (struct sockaddr *)&sa, 1);
+ ret = io_uring_submit(&ring);
+ if (ret < 0) {
+ fprintf(stderr, "stale: submit step2: %s\n",
+ strerror(-ret));
+ close(fd);
+ exit(1);
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "stale: wait step2: %s\n",
+ strerror(-ret));
+ close(fd);
+ exit(1);
+ }
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr, "stale: short-len expected -EINVAL, got %d\n",
+ cqe->res);
+ failed++;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ close(fd);
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_deny_family(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in v4 = {
+ .sin_family = AF_INET,
+ .sin_port = htons(1),
+ };
+ struct sockaddr_in6 v6 = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(1),
+ };
+ struct sockaddr_un un = { .sun_family = AF_UNIX };
+
+ v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ v6.sin6_addr = in6addr_loopback;
+ strncpy(un.sun_path, "/tmp/cbpf_filter_no_such_socket",
+ sizeof(un.sun_path) - 1);
+
+ ret = register_bpf_filter(connect_deny_family_filter,
+ sizeof(connect_deny_family_filter) / sizeof(connect_deny_family_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&v4, sizeof(v4),
+ "AF_INET should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&v6, sizeof(v6),
+ "AF_INET6 should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&un, sizeof(un),
+ "AF_UNIX should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_allow_v4_addr(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in allowed = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+ struct sockaddr_in denied_a = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+ struct sockaddr_in denied_b = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+
+ allowed.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ denied_a.sin_addr.s_addr = htonl(0x7f00007f);
+ denied_b.sin_addr.s_addr = htonl(0x7f000002);
+
+ ret = register_bpf_filter(connect_allow_v4_addr_filter,
+ sizeof(connect_allow_v4_addr_filter) / sizeof(connect_allow_v4_addr_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&allowed, sizeof(allowed),
+ "127.0.0.1 should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&denied_a, sizeof(denied_a),
+ "127.0.0.127 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&denied_b, sizeof(denied_b),
+ "127.0.0.2 should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Test blacklisting the v6 address 2001:db8::dead for
+ * IORING_OP_CONNECT. Other v6 addresses (including those sharing the
+ * 2001:db8::/32 prefix) are allowed. Non-AF_INET6 sockaddrs fall
+ * through to allow as well, since this is purely a v6-address
+ * blacklist.
+ */
+static int test_connect_deny_v6_addr(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in6 banned = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+ struct sockaddr_in6 other_lo = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+ struct sockaddr_in6 other_doc = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+
+ /* 2001:db8::dead -- banned */
+ banned.sin6_addr.s6_addr[0] = 0x20;
+ banned.sin6_addr.s6_addr[1] = 0x01;
+ banned.sin6_addr.s6_addr[2] = 0x0d;
+ banned.sin6_addr.s6_addr[3] = 0xb8;
+ banned.sin6_addr.s6_addr[14] = 0xde;
+ banned.sin6_addr.s6_addr[15] = 0xad;
+ /* ::1 -- loopback, outside the banned exact address */
+ other_lo.sin6_addr = in6addr_loopback;
+ /* 2001:db8::1 -- same /32 prefix, different exact addr */
+ other_doc.sin6_addr.s6_addr[0] = 0x20;
+ other_doc.sin6_addr.s6_addr[1] = 0x01;
+ other_doc.sin6_addr.s6_addr[2] = 0x0d;
+ other_doc.sin6_addr.s6_addr[3] = 0xb8;
+ other_doc.sin6_addr.s6_addr[15] = 0x01;
+
+ ret = register_bpf_filter(connect_deny_v6_addr_filter,
+ sizeof(connect_deny_v6_addr_filter) / sizeof(connect_deny_v6_addr_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&banned, sizeof(banned),
+ "2001:db8::dead should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&other_lo, sizeof(other_lo),
+ "::1 should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&other_doc, sizeof(other_doc),
+ "2001:db8::1 should be allowed", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_allow_v6_addr(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in6 allowed = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+ struct sockaddr_in6 denied_lo = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+ struct sockaddr_in6 denied_doc = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+
+ allowed.sin6_addr = in6addr_loopback;
+ /* ::2 -- test target */
+ denied_lo.sin6_addr.s6_addr[15] = 0x02;
+ /* 2001:db8::1 -- test target */
+ denied_doc.sin6_addr.s6_addr[0] = 0x20;
+ denied_doc.sin6_addr.s6_addr[1] = 0x01;
+ denied_doc.sin6_addr.s6_addr[2] = 0x0d;
+ denied_doc.sin6_addr.s6_addr[3] = 0xb8;
+ denied_doc.sin6_addr.s6_addr[15] = 0x01;
+
+ ret = register_bpf_filter(connect_allow_v6_addr_filter,
+ sizeof(connect_allow_v6_addr_filter) / sizeof(connect_allow_v6_addr_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&allowed, sizeof(allowed),
+ "::1 should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&denied_lo, sizeof(denied_lo),
+ "::2 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&denied_doc, sizeof(denied_doc),
+ "2001:db8::1 should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_allow_port(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in allowed = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+ struct sockaddr_in denied_ssh = {
+ .sin_family = AF_INET,
+ .sin_port = htons(22),
+ };
+ struct sockaddr_in denied_https = {
+ .sin_family = AF_INET,
+ .sin_port = htons(443),
+ };
+
+ allowed.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ denied_ssh.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ denied_https.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ ret = register_bpf_filter(connect_allow_port_filter,
+ sizeof(connect_allow_port_filter) / sizeof(connect_allow_port_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&allowed, sizeof(allowed),
+ "port 80 should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&denied_ssh, sizeof(denied_ssh),
+ "port 22 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&denied_https, sizeof(denied_https),
+ "port 443 should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_deny_v4_subnet(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in in_subnet_a = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+ struct sockaddr_in in_subnet_b = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+ struct sockaddr_in out_subnet = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+
+ in_subnet_a.sin_addr.s_addr = htonl(0x7f2a0001); /* 127.42.0.1 */
+ in_subnet_b.sin_addr.s_addr = htonl(0x7f2a0063); /* 127.42.0.99 */
+ out_subnet.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ ret = register_bpf_filter(connect_deny_v4_subnet_filter,
+ sizeof(connect_deny_v4_subnet_filter) / sizeof(connect_deny_v4_subnet_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&in_subnet_a, sizeof(in_subnet_a),
+ "127.42.0.1 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&in_subnet_b, sizeof(in_subnet_b),
+ "127.42.0.99 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&out_subnet, sizeof(out_subnet),
+ "127.0.0.1 should be allowed", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_allow_v4_subnet(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in in_subnet_a = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+ struct sockaddr_in in_subnet_b = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+ struct sockaddr_in out_subnet = {
+ .sin_family = AF_INET,
+ .sin_port = htons(80),
+ };
+
+ in_subnet_a.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* 127.0.0.1 */
+ in_subnet_b.sin_addr.s_addr = htonl(0x7f000063); /* 127.0.0.99 */
+ out_subnet.sin_addr.s_addr = htonl(0x7f2a0001); /* 127.42.0.1 */
+
+ ret = register_bpf_filter(connect_allow_v4_subnet_filter,
+ sizeof(connect_allow_v4_subnet_filter) / sizeof(connect_allow_v4_subnet_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&in_subnet_a, sizeof(in_subnet_a),
+ "127.0.0.1 should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&in_subnet_b, sizeof(in_subnet_b),
+ "127.0.0.99 should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&out_subnet, sizeof(out_subnet),
+ "127.42.0.1 should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_deny_v6_subnet(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in6 in_subnet_a = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+ struct sockaddr_in6 in_subnet_b = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+ struct sockaddr_in6 out_subnet = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+
+ /* 2001:db8::1 */
+ in_subnet_a.sin6_addr.s6_addr[0] = 0x20;
+ in_subnet_a.sin6_addr.s6_addr[1] = 0x01;
+ in_subnet_a.sin6_addr.s6_addr[2] = 0x0d;
+ in_subnet_a.sin6_addr.s6_addr[3] = 0xb8;
+ in_subnet_a.sin6_addr.s6_addr[15] = 0x01;
+ /* 2001:db8:dead::1 -- same /32 prefix, different remainder */
+ in_subnet_b.sin6_addr.s6_addr[0] = 0x20;
+ in_subnet_b.sin6_addr.s6_addr[1] = 0x01;
+ in_subnet_b.sin6_addr.s6_addr[2] = 0x0d;
+ in_subnet_b.sin6_addr.s6_addr[3] = 0xb8;
+ in_subnet_b.sin6_addr.s6_addr[4] = 0xde;
+ in_subnet_b.sin6_addr.s6_addr[5] = 0xad;
+ in_subnet_b.sin6_addr.s6_addr[15] = 0x01;
+ /* ::1 -- loopback, outside the /32 */
+ out_subnet.sin6_addr = in6addr_loopback;
+
+ ret = register_bpf_filter(connect_deny_v6_subnet_filter,
+ sizeof(connect_deny_v6_subnet_filter) / sizeof(connect_deny_v6_subnet_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&in_subnet_a, sizeof(in_subnet_a),
+ "2001:db8::1 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&in_subnet_b, sizeof(in_subnet_b),
+ "2001:db8:dead::1 should be denied", 0) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&out_subnet, sizeof(out_subnet),
+ "::1 should be allowed", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+static int test_connect_allow_v6_subnet(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ struct sockaddr_in6 in_subnet_a = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+ struct sockaddr_in6 in_subnet_b = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+ struct sockaddr_in6 out_subnet = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(80),
+ };
+
+ /* fe80::1 */
+ in_subnet_a.sin6_addr.s6_addr[0] = 0xfe;
+ in_subnet_a.sin6_addr.s6_addr[1] = 0x80;
+ in_subnet_a.sin6_addr.s6_addr[15] = 0x01;
+ /* fe80:cafe::beef -- same /16 prefix */
+ in_subnet_b.sin6_addr.s6_addr[0] = 0xfe;
+ in_subnet_b.sin6_addr.s6_addr[1] = 0x80;
+ in_subnet_b.sin6_addr.s6_addr[2] = 0xca;
+ in_subnet_b.sin6_addr.s6_addr[3] = 0xfe;
+ in_subnet_b.sin6_addr.s6_addr[14] = 0xbe;
+ in_subnet_b.sin6_addr.s6_addr[15] = 0xef;
+ /* 2001:db8::1 -- documentation prefix, outside fe80::/16 */
+ out_subnet.sin6_addr.s6_addr[0] = 0x20;
+ out_subnet.sin6_addr.s6_addr[1] = 0x01;
+ out_subnet.sin6_addr.s6_addr[2] = 0x0d;
+ out_subnet.sin6_addr.s6_addr[3] = 0xb8;
+ out_subnet.sin6_addr.s6_addr[15] = 0x01;
+
+ ret = register_bpf_filter(connect_allow_v6_subnet_filter,
+ sizeof(connect_allow_v6_subnet_filter) / sizeof(connect_allow_v6_subnet_filter[0]),
+ IORING_OP_CONNECT, CONNECT_PDU_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: register failed: %s\n",
+ strerror(-ret));
+ exit(ret == -EINVAL ? 0 : 1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child: queue_init failed\n");
+ exit(1);
+ }
+
+ if (test_connect(&ring, (struct sockaddr *)&in_subnet_a, sizeof(in_subnet_a),
+ "fe80::1 should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&in_subnet_b, sizeof(in_subnet_b),
+ "fe80:cafe::beef should be allowed", 1) != 0)
+ failed++;
+ if (test_connect(&ring, (struct sockaddr *)&out_subnet, sizeof(out_subnet),
+ "2001:db8::1 should be denied", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Ring-level filter tests - these test filters registered on a specific ring
+ * rather than on the task. Ring filters don't require forking.
+ */
+
+static int test_deny_nop_ring(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ ret = register_bpf_filter_ring(&ring, deny_all_filter,
+ ARRAY_SIZE(deny_all_filter),
+ IORING_OP_NOP, 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "register failed: %s\n", strerror(-ret));
+ io_uring_queue_exit(&ring);
+ return ret == -EINVAL ? 0 : 1;
+ }
+
+ if (test_nop(&ring, "NOP should be denied (ring)", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ return failed;
+}
+
+static int test_allow_inet_only_ring(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ ret = register_bpf_filter_ring(&ring, allow_inet_only_filter,
+ ARRAY_SIZE(allow_inet_only_filter),
+ IORING_OP_SOCKET, 12, 0);
+ if (ret < 0) {
+ fprintf(stderr, "register failed: %s\n", strerror(-ret));
+ io_uring_queue_exit(&ring);
+ return ret == -EINVAL ? 0 : 1;
+ }
+
+ if (test_socket(&ring, AF_INET, SOCK_STREAM,
+ "AF_INET TCP should succeed (ring)", 1) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_INET6, SOCK_STREAM,
+ "AF_INET6 TCP should be denied (ring)", 0) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_UNIX, SOCK_STREAM,
+ "AF_UNIX should be denied (ring)", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ return failed;
+}
+
+static int test_allow_tcp_only_ring(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ ret = register_bpf_filter_ring(&ring, allow_tcp_only_filter,
+ ARRAY_SIZE(allow_tcp_only_filter),
+ IORING_OP_SOCKET, 12, 0);
+ if (ret < 0) {
+ fprintf(stderr, "register failed: %s\n", strerror(-ret));
+ io_uring_queue_exit(&ring);
+ return ret == -EINVAL ? 0 : 1;
+ }
+
+ if (test_socket(&ring, AF_INET, SOCK_STREAM,
+ "TCP should succeed (ring)", 1) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_INET, SOCK_DGRAM,
+ "UDP should be denied (ring)", 0) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_INET6, SOCK_STREAM,
+ "IPv6 TCP should succeed (ring)", 1) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ return failed;
+}
+
+static int test_deny_rest_ring(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ /* Register allow filter for NOP with DENY_REST flag */
+ ret = register_bpf_filter_ring(&ring, allow_all_filter,
+ ARRAY_SIZE(allow_all_filter),
+ IORING_OP_NOP, 0, 1);
+ if (ret < 0) {
+ fprintf(stderr, "register failed: %s\n", strerror(-ret));
+ io_uring_queue_exit(&ring);
+ return ret == -EINVAL ? 0 : 1;
+ }
+
+ if (test_nop(&ring, "NOP should succeed (ring)", 1) != 0)
+ failed++;
+
+ if (test_socket(&ring, AF_INET, SOCK_STREAM,
+ "Socket should be denied DENY_REST (ring)", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ return failed;
+}
+
+/*
+ * Test pdu_size validation for filter registration.
+ *
+ * IORING_OP_SOCKET has a kernel pdu_size of 12 (3x __u32). Test:
+ * 1) pdu_size too big (24) - should fail with -EMSGSIZE
+ * 2) pdu_size too small (8) without strict - should succeed, kernel
+ * writes back actual pdu_size (12)
+ * 3) pdu_size too small (8) with IO_URING_BPF_FILTER_SZ_STRICT -
+ * should fail with -EMSGSIZE, kernel writes back actual pdu_size (12)
+ */
+static int test_pdu_size_ring(void)
+{
+ struct io_uring ring;
+ struct io_uring_bpf bpf;
+ int ret, failed = 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ /* Test 1: pdu_size too big, should fail with -EMSGSIZE */
+ memset(&bpf, 0, sizeof(bpf));
+ bpf.cmd_type = IO_URING_BPF_CMD_FILTER;
+ bpf.filter.opcode = IORING_OP_SOCKET;
+ bpf.filter.filter_len = ARRAY_SIZE(allow_all_filter);
+ bpf.filter.filter_ptr = (unsigned long) (uintptr_t) allow_all_filter;
+ bpf.filter.pdu_size = 24;
+
+ ret = io_uring_register_bpf_filter(&ring, &bpf);
+ if (ret != -EMSGSIZE) {
+ fprintf(stderr, "pdu too big: expected -EMSGSIZE, got %d\n",
+ ret);
+ failed++;
+ } else if (bpf.filter.pdu_size != 12) {
+ fprintf(stderr, "pdu too big: expected writeback 12, got %u\n",
+ bpf.filter.pdu_size);
+ failed++;
+ }
+
+ /* Test 2: pdu_size smaller without strict, should succeed */
+ memset(&bpf, 0, sizeof(bpf));
+ bpf.cmd_type = IO_URING_BPF_CMD_FILTER;
+ bpf.filter.opcode = IORING_OP_SOCKET;
+ bpf.filter.filter_len = ARRAY_SIZE(allow_all_filter);
+ bpf.filter.filter_ptr = (unsigned long) (uintptr_t) allow_all_filter;
+ bpf.filter.pdu_size = 8;
+
+ ret = io_uring_register_bpf_filter(&ring, &bpf);
+ if (ret) {
+ fprintf(stderr, "pdu smaller no strict: expected success, "
+ "got %d\n", ret);
+ failed++;
+ } else if (bpf.filter.pdu_size != 12) {
+ fprintf(stderr, "pdu smaller no strict: expected writeback "
+ "12, got %u\n", bpf.filter.pdu_size);
+ failed++;
+ }
+
+ io_uring_queue_exit(&ring);
+
+ /*
+ * Test 3: pdu_size smaller with strict, should fail.
+ * Use a fresh ring since test 2 registered a filter.
+ */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "queue_init 2 failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ memset(&bpf, 0, sizeof(bpf));
+ bpf.cmd_type = IO_URING_BPF_CMD_FILTER;
+ bpf.filter.opcode = IORING_OP_SOCKET;
+ bpf.filter.flags = IO_URING_BPF_FILTER_SZ_STRICT;
+ bpf.filter.filter_len = ARRAY_SIZE(allow_all_filter);
+ bpf.filter.filter_ptr = (unsigned long) (uintptr_t) allow_all_filter;
+ bpf.filter.pdu_size = 8;
+
+ ret = io_uring_register_bpf_filter(&ring, &bpf);
+ if (ret != -EMSGSIZE) {
+ fprintf(stderr, "pdu smaller strict: expected -EMSGSIZE, "
+ "got %d\n", ret);
+ failed++;
+ } else if (bpf.filter.pdu_size != 12) {
+ fprintf(stderr, "pdu smaller strict: expected writeback 12, "
+ "got %u\n", bpf.filter.pdu_size);
+ failed++;
+ }
+
+ io_uring_queue_exit(&ring);
+ return failed;
+}
+
+/*
+ * Test that child processes inherit parent's restrictions.
+ * Parent registers a filter, forks, child verifies the restriction applies.
+ */
+static int test_inherit_restrictions(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* First child: register deny filter for NOP, then fork */
+ ret = register_bpf_filter(deny_all_filter,
+ sizeof(deny_all_filter) / sizeof(deny_all_filter[0]),
+ IORING_OP_NOP, 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child1: register failed: %s\n",
+ strerror(-ret));
+ exit(1);
+ }
+
+ /* Fork grandchild to test inheritance */
+ pid_t grandchild = fork();
+ if (grandchild < 0) {
+ perror("fork grandchild");
+ exit(1);
+ }
+
+ if (grandchild == 0) {
+ /* Grandchild: should inherit parent's NOP denial */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Grandchild: queue_init failed\n");
+ exit(1);
+ }
+
+ /* NOP should be denied due to inherited restriction */
+ if (test_nop(&ring, "inherited NOP denial", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(grandchild, &status, 0);
+ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Test that child can add new restrictions on top of inherited ones.
+ * Parent allows only AF_INET, child adds TCP-only filter.
+ * Result: only AF_INET + TCP should be allowed.
+ */
+static int test_stack_restrictions(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* First child: register AF_INET only filter */
+ ret = register_bpf_filter(allow_inet_only_filter,
+ sizeof(allow_inet_only_filter) / sizeof(allow_inet_only_filter[0]),
+ IORING_OP_SOCKET, 12, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child1: register failed: %s\n",
+ strerror(-ret));
+ exit(1);
+ }
+
+ /* Fork grandchild to add more restrictions */
+ pid_t grandchild = fork();
+ if (grandchild < 0) {
+ perror("fork grandchild");
+ exit(1);
+ }
+
+ if (grandchild == 0) {
+ /* Grandchild: add TCP-only filter on top */
+ ret = register_bpf_filter(allow_tcp_only_filter,
+ sizeof(allow_tcp_only_filter) / sizeof(allow_tcp_only_filter[0]),
+ IORING_OP_SOCKET, 12, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Grandchild: register failed: %s\n",
+ strerror(-ret));
+ exit(1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Grandchild: queue_init failed\n");
+ exit(1);
+ }
+
+ /* AF_INET + TCP: allowed by both filters */
+ if (test_socket(&ring, AF_INET, SOCK_STREAM,
+ "AF_INET TCP (both filters allow)", 1) != 0)
+ failed++;
+
+ /* AF_INET + UDP: allowed by parent, denied by child */
+ if (test_socket(&ring, AF_INET, SOCK_DGRAM,
+ "AF_INET UDP (child denies)", 0) != 0)
+ failed++;
+
+ /* AF_INET6 + TCP: denied by parent, allowed by child */
+ if (test_socket(&ring, AF_INET6, SOCK_STREAM,
+ "AF_INET6 TCP (parent denies)", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(grandchild, &status, 0);
+ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Test that child cannot loosen parent's restrictions.
+ * Parent denies NOP, child tries to allow it - should still be denied.
+ */
+static int test_cannot_loosen_restrictions(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* First child: deny NOP */
+ ret = register_bpf_filter(deny_all_filter,
+ sizeof(deny_all_filter) / sizeof(deny_all_filter[0]),
+ IORING_OP_NOP, 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Child1: register failed: %s\n",
+ strerror(-ret));
+ exit(1);
+ }
+
+ /* Fork grandchild that tries to allow NOP */
+ pid_t grandchild = fork();
+ if (grandchild < 0) {
+ perror("fork grandchild");
+ exit(1);
+ }
+
+ if (grandchild == 0) {
+ /* Grandchild: try to allow NOP (inherits no_new_privs) */
+ ret = register_bpf_filter(allow_all_filter,
+ sizeof(allow_all_filter) / sizeof(allow_all_filter[0]),
+ IORING_OP_NOP, 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Grandchild: register failed: %s\n",
+ strerror(-ret));
+ exit(1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Grandchild: queue_init failed\n");
+ exit(1);
+ }
+
+ /*
+ * NOP should still be denied - child's allow filter
+ * runs first (returns 1), but parent's deny filter
+ * is stacked and runs second (returns 0).
+ */
+ if (test_nop(&ring, "NOP still denied (can't loosen)", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(grandchild, &status, 0);
+ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Test multi-level inheritance (parent -> child -> grandchild -> great-grandchild).
+ * Each level adds more restrictions.
+ */
+static int test_multi_level_inherit(void)
+{
+ struct io_uring ring;
+ int ret, failed = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* Level 1: allow only AF_INET for sockets */
+ ret = register_bpf_filter(allow_inet_only_filter,
+ sizeof(allow_inet_only_filter) / sizeof(allow_inet_only_filter[0]),
+ IORING_OP_SOCKET, 12, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Level1: register failed\n");
+ exit(1);
+ }
+
+ pid_t level2 = fork();
+ if (level2 < 0)
+ exit(1);
+
+ if (level2 == 0) {
+ /* Level 2: add TCP-only restriction */
+ ret = register_bpf_filter(allow_tcp_only_filter,
+ sizeof(allow_tcp_only_filter) / sizeof(allow_tcp_only_filter[0]),
+ IORING_OP_SOCKET, 12, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Level2: register failed\n");
+ exit(1);
+ }
+
+ pid_t level3 = fork();
+ if (level3 < 0)
+ exit(1);
+
+ if (level3 == 0) {
+ /* Level 3: allow NOP with DENY_REST */
+ ret = register_bpf_filter(allow_all_filter,
+ sizeof(allow_all_filter) / sizeof(allow_all_filter[0]),
+ IORING_OP_NOP, 0, 1);
+ if (ret < 0) {
+ fprintf(stderr, "Level3: register failed\n");
+ exit(1);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Level3: queue_init failed\n");
+ exit(1);
+ }
+
+ /* NOP: allowed (explicit filter) */
+ if (test_nop(&ring, "NOP allowed", 1) != 0)
+ failed++;
+
+ /*
+ * Socket still governed by inherited filters.
+ * DENY_REST only denies opcodes with no filter
+ * in the entire chain - ancestors have socket
+ * filters so those still apply.
+ *
+ * AF_INET + TCP: allowed by both ancestors
+ */
+ if (test_socket(&ring, AF_INET, SOCK_STREAM,
+ "AF_INET TCP (inherited filters)", 1) != 0)
+ failed++;
+
+ /* AF_INET + UDP: denied by Level 2's TCP filter */
+ if (test_socket(&ring, AF_INET, SOCK_DGRAM,
+ "AF_INET UDP (denied by L2)", 0) != 0)
+ failed++;
+
+ /* AF_INET6 + TCP: denied by Level 1's AF_INET filter */
+ if (test_socket(&ring, AF_INET6, SOCK_STREAM,
+ "AF_INET6 TCP (denied by L1)", 0) != 0)
+ failed++;
+
+ io_uring_queue_exit(&ring);
+ exit(failed);
+ }
+
+ waitpid(level3, &status, 0);
+ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
+ }
+
+ waitpid(level2, &status, 0);
+ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return 1;
+}
+
+/*
+ * Test that registering a filter without no_new_privs returns -EACCES.
+ * This must be called before prctl(PR_SET_NO_NEW_PRIVS) in main().
+ */
+static int test_no_new_privs_required(void)
+{
+ struct io_uring_bpf io_bpf = {
+ .cmd_type = IO_URING_BPF_CMD_FILTER,
+ .filter = {
+ .opcode = IORING_OP_NOP,
+ .flags = 0,
+ .filter_len = sizeof(allow_all_filter) / sizeof(allow_all_filter[0]),
+ .filter_ptr = (unsigned long)allow_all_filter,
+ },
+ };
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ int ret;
+
+ /* Try to register without no_new_privs - should fail with EACCES */
+ ret = io_uring_register(-1, IORING_REGISTER_BPF_FILTER,
+ &io_bpf, 1);
+ if (ret == -EACCES) {
+ if (!geteuid())
+ exit(1);
+ exit(0); /* Expected */
+ } else if (!ret) {
+ if (!geteuid())
+ exit(0);
+ exit(1);
+ }
+ if (ret == -EINVAL || ret == -ENOSYS)
+ exit(2); /* Not supported */
+ fprintf(stderr, "Expected -EACCES, got %d\n", ret);
+ exit(1);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ int code = WEXITSTATUS(status);
+ if (code == 0)
+ return 0; /* Test passed */
+ if (code == 2)
+ return -1; /* Not supported, skip */
+ }
+ fprintf(stderr, "test_no_new_privs_required failed\n");
+ return 1;
+}
+
+static int probe_bpf_filter_support(void)
+{
+ struct io_uring_bpf io_bpf = {
+ .cmd_type = IO_URING_BPF_CMD_FILTER,
+ .filter = {
+ .opcode = IORING_OP_NOP,
+ .flags = 0,
+ .filter_len = sizeof(allow_all_filter) / sizeof(allow_all_filter[0]),
+ .filter_ptr = (unsigned long)allow_all_filter,
+ },
+ };
+ pid_t pid;
+ int status;
+
+ /* Fork so we don't pollute the main process */
+ pid = fork();
+ if (pid < 0)
+ return -1;
+
+ if (pid == 0) {
+ int ret;
+
+ ret = io_uring_register(-1, IORING_REGISTER_BPF_FILTER,
+ &io_bpf, 1);
+ exit(ret < 0 ? -ret : 0);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ int code = WEXITSTATUS(status);
+ if (code == EINVAL || code == ENOSYS)
+ return -1; /* Not supported */
+ return 0; /* Supported (or other error we'll catch later) */
+ }
+ return -1;
+}
+
+static int probe_connect_filter_support(void)
+{
+ struct io_uring_bpf io_bpf = {
+ .cmd_type = IO_URING_BPF_CMD_FILTER,
+ .filter = {
+ .opcode = IORING_OP_CONNECT,
+ .flags = 0,
+ .filter_len = sizeof(allow_all_filter) / sizeof(allow_all_filter[0]),
+ .filter_ptr = (unsigned long)allow_all_filter,
+ .pdu_size = CONNECT_PDU_SIZE,
+ },
+ };
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0)
+ return -1;
+
+ if (pid == 0) {
+ int ret;
+
+ ret = io_uring_register(-1, IORING_REGISTER_BPF_FILTER,
+ &io_bpf, 1);
+ exit(ret < 0 ? -ret : 0);
+ }
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ int code = WEXITSTATUS(status);
+ if (code == EMSGSIZE)
+ return -1; /* No populator for IORING_OP_CONNECT */
+ return 0; /* Populator present (or unrelated error we will catch later) */
+ }
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ int total_failed = 0;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ if (probe_bpf_filter_support() < 0)
+ return T_EXIT_SKIP;
+
+ /*
+ * Test that filter registration fails without no_new_privs.
+ * Must run before we call prctl() below.
+ */
+ ret = test_no_new_privs_required();
+ if (ret < 0)
+ return T_EXIT_SKIP;
+ if (ret > 0)
+ total_failed++;
+
+ /*
+ * Must set no_new_privs to register BPF filters without CAP_SYS_ADMIN.
+ * This is inherited by all child processes.
+ */
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+ perror("prctl");
+ return T_EXIT_SKIP;
+ }
+
+ /* Task-level filter tests */
+ total_failed += test_deny_nop();
+ total_failed += test_allow_inet_only();
+ total_failed += test_allow_tcp_only();
+ total_failed += test_deny_rest();
+
+ /* Task-level openat/openat2 filter tests */
+ total_failed += test_deny_openat_creat();
+ total_failed += test_deny_openat2_resolve_in_root();
+
+ /* Task-level connect filter tests */
+ /*
+ * Probe whether the kernel exposes a filter populator for
+ * IORING_OP_CONNECT.
+ */
+ if (probe_connect_filter_support() == 0) {
+ total_failed += test_connect_allow_family();
+ total_failed += test_connect_deny_family();
+ total_failed += test_connect_deny_v4_addr();
+ total_failed += test_connect_deny_port();
+ total_failed += test_connect_stale_addr_len();
+ total_failed += test_connect_allow_v4_addr();
+ total_failed += test_connect_deny_v6_addr();
+ total_failed += test_connect_allow_v6_addr();
+ total_failed += test_connect_allow_port();
+ total_failed += test_connect_deny_v4_subnet();
+ total_failed += test_connect_allow_v4_subnet();
+ total_failed += test_connect_deny_v6_subnet();
+ total_failed += test_connect_allow_v6_subnet();
+ }
+
+ /* Ring-level filter tests */
+ total_failed += test_deny_nop_ring();
+ total_failed += test_allow_inet_only_ring();
+ total_failed += test_allow_tcp_only_ring();
+ total_failed += test_deny_rest_ring();
+ total_failed += test_pdu_size_ring();
+
+ /* Per-task inheritance tests */
+ total_failed += test_inherit_restrictions();
+ total_failed += test_stack_restrictions();
+ total_failed += test_cannot_loosen_restrictions();
+ total_failed += test_multi_level_inherit();
+
+ return total_failed;
+}
diff --git a/contrib/libs/liburing/test/cbpf_filter.t/ya.make b/contrib/libs/liburing/test/cbpf_filter.t/ya.make
new file mode 100644
index 00000000000..4d6e68b24c0
--- /dev/null
+++ b/contrib/libs/liburing/test/cbpf_filter.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ cbpf_filter.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/ce593a6c480a.t/ya.make b/contrib/libs/liburing/test/ce593a6c480a.t/ya.make
index 3564ca9a39e..7952f9815d4 100644
--- a/contrib/libs/liburing/test/ce593a6c480a.t/ya.make
+++ b/contrib/libs/liburing/test/ce593a6c480a.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/close-opath.t/ya.make b/contrib/libs/liburing/test/close-opath.t/ya.make
index 9ce14c793b7..236b0736b83 100644
--- a/contrib/libs/liburing/test/close-opath.t/ya.make
+++ b/contrib/libs/liburing/test/close-opath.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/cmd-discard.t/ya.make b/contrib/libs/liburing/test/cmd-discard.t/ya.make
index 90c4bbb89db..4216421238b 100644
--- a/contrib/libs/liburing/test/cmd-discard.t/ya.make
+++ b/contrib/libs/liburing/test/cmd-discard.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/conn-unreach.c b/contrib/libs/liburing/test/conn-unreach.c
index fb38de03d29..3b6fd63d048 100644
--- a/contrib/libs/liburing/test/conn-unreach.c
+++ b/contrib/libs/liburing/test/conn-unreach.c
@@ -37,7 +37,7 @@ static int check_cqe(struct io_uring *ring, struct io_uring_cqe *cqe)
}
break;
case 2:
- if (cqe->res) {
+ if (cqe->res && cqe->res != -ENOTCONN) {
fprintf(stderr, "Unexpected shutdown: %d\n", cqe->res);
return T_EXIT_FAIL;
}
diff --git a/contrib/libs/liburing/test/conn-unreach.t/ya.make b/contrib/libs/liburing/test/conn-unreach.t/ya.make
index ca9c87289e8..59506e7d2f1 100644
--- a/contrib/libs/liburing/test/conn-unreach.t/ya.make
+++ b/contrib/libs/liburing/test/conn-unreach.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/connect-rep.c b/contrib/libs/liburing/test/connect-rep.c
index ca300e7ac36..613e2a6f0ed 100644
--- a/contrib/libs/liburing/test/connect-rep.c
+++ b/contrib/libs/liburing/test/connect-rep.c
@@ -120,11 +120,7 @@ static int test_connect(struct io_uring *ring,
memcpy(&local_sa, serv_addr, sizeof(local_sa));
- ret = io_uring_submit_and_wait(ring, 1);
- if (ret != 1) {
- fprintf(stderr, "submit=%d\n", ret);
- return T_EXIT_FAIL;
- }
+ io_uring_submit_and_wait(ring, 1);
/* check for reuse at the same time */
memset(&local_sa, 0xff, sizeof(local_sa));
diff --git a/contrib/libs/liburing/test/connect-rep.t/ya.make b/contrib/libs/liburing/test/connect-rep.t/ya.make
index 7f0649dbbc2..3727fafea6f 100644
--- a/contrib/libs/liburing/test/connect-rep.t/ya.make
+++ b/contrib/libs/liburing/test/connect-rep.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/connect.t/ya.make b/contrib/libs/liburing/test/connect.t/ya.make
index 04cda71f4f3..1042bc665cd 100644
--- a/contrib/libs/liburing/test/connect.t/ya.make
+++ b/contrib/libs/liburing/test/connect.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/coredump.t/ya.make b/contrib/libs/liburing/test/coredump.t/ya.make
index f6495341aad..93b484506c6 100644
--- a/contrib/libs/liburing/test/coredump.t/ya.make
+++ b/contrib/libs/liburing/test/coredump.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/cq-full.t/ya.make b/contrib/libs/liburing/test/cq-full.t/ya.make
index 5408ddc6a1a..51a1b2228d4 100644
--- a/contrib/libs/liburing/test/cq-full.t/ya.make
+++ b/contrib/libs/liburing/test/cq-full.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/cq-overflow.t/ya.make b/contrib/libs/liburing/test/cq-overflow.t/ya.make
index a3dc746a9a1..14c9c504010 100644
--- a/contrib/libs/liburing/test/cq-overflow.t/ya.make
+++ b/contrib/libs/liburing/test/cq-overflow.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/cq-peek-batch-mixed.c b/contrib/libs/liburing/test/cq-peek-batch-mixed.c
new file mode 100644
index 00000000000..ff69e5fd550
--- /dev/null
+++ b/contrib/libs/liburing/test/cq-peek-batch-mixed.c
@@ -0,0 +1,190 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test CQ peek-batch on rings setup with IORING_SETUP_CQE_MIXED,
+ * where CQEs are variably sized and the kernel may post skip entries to pad
+ * the CQ ring at wrap time.
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int queue_nops(struct io_uring *ring, int n, int cqe32, int offset)
+{
+ struct io_uring_sqe *sqe;
+ int i, ret;
+
+ for (i = 0; i < n; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->user_data = i + offset;
+ if (cqe32) {
+ sqe->nop_flags = IORING_NOP_CQE32;
+ sqe->off = i + offset + 1000;
+ sqe->addr = i + offset + 2000;
+ }
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != n) {
+ fprintf(stderr, "submitted %d, wanted %d\n", ret, n);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int check_cqe(struct io_uring_cqe *cqe, __u64 user_data, int cqe32)
+{
+ if (cqe->flags & IORING_CQE_F_SKIP) {
+ fprintf(stderr, "skip CQE returned to application\n");
+ return 1;
+ }
+ if (cqe->user_data != user_data) {
+ fprintf(stderr, "got user_data %ld, expected %ld\n",
+ (long) cqe->user_data, (long) user_data);
+ return 1;
+ }
+ if (cqe32 != !!(cqe->flags & IORING_CQE_F_32)) {
+ fprintf(stderr, "bad CQE_F_32 flag for user_data %ld\n",
+ (long) cqe->user_data);
+ return 1;
+ }
+ if (cqe32) {
+ if (cqe->big_cqe[0] != user_data + 1000 ||
+ cqe->big_cqe[1] != user_data + 2000) {
+ fprintf(stderr, "bad big_cqe data for user_data %ld\n",
+ (long) cqe->user_data);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void advance_cqes(struct io_uring *ring, struct io_uring_cqe **cqes,
+ unsigned nr)
+{
+ unsigned i, slots = 0;
+
+ for (i = 0; i < nr; i++)
+ slots += io_uring_cqe_nr(cqes[i]);
+
+ io_uring_cq_advance(ring, slots);
+}
+
+#define CHECK_BATCH(ring, got, cqes, count, expected) do {\
+ got = io_uring_peek_batch_cqe((ring), cqes, count);\
+ if (got != expected) {\
+ fprintf(stderr, "got %d CQEs, expected %d\n", got, expected);\
+ goto err;\
+ }\
+} while(0)
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = { };
+ struct io_uring_cqe *cqes[16];
+ struct io_uring ring;
+ unsigned got;
+ int ret, i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ p.flags = IORING_SETUP_CQE_MIXED;
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ /* the wrap arithmetic below assumes 16 CQ ring slots */
+ if (ring.cq.ring_entries != 16) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+ }
+
+ CHECK_BATCH(&ring, got, cqes, 16, 0);
+
+ /*
+ * Mix of 16b and 32b CQEs, 8 slots in total. The batch must return
+ * pointers to the CQE starts, not one pointer per slot.
+ */
+ if (queue_nops(&ring, 2, 0, 1))
+ goto err;
+ if (queue_nops(&ring, 2, 1, 3))
+ goto err;
+ if (queue_nops(&ring, 2, 0, 5))
+ goto err;
+
+ /* limited count batch, ending on a 32b CQE */
+ CHECK_BATCH(&ring, got, cqes, 3, 3);
+
+ CHECK_BATCH(&ring, got, cqes, 16, 6);
+ for (i = 0; i < 6; i++) {
+ int cqe32 = i == 2 || i == 3;
+
+ if (check_cqe(cqes[i], i + 1, cqe32))
+ goto err;
+ }
+ advance_cqes(&ring, cqes, got);
+
+ /*
+ * CQ head is now at slot 8 of 16. Consume 3 more plain CQEs to move
+ * it to slot 11, then fill slots 11-14 with plain CQEs and post a
+ * 32b CQE. That doesn't fit in the single slot left before the ring
+ * wraps, so the kernel posts a skip entry in slot 15 and the 32b CQE
+ * in slots 0-1. Top it off with a plain CQE in slot 2.
+ */
+ if (queue_nops(&ring, 3, 0, 10))
+ goto err;
+ CHECK_BATCH(&ring, got, cqes, 16, 3);
+ advance_cqes(&ring, cqes, got);
+
+ if (queue_nops(&ring, 4, 0, 20))
+ goto err;
+ if (queue_nops(&ring, 1, 1, 24))
+ goto err;
+ if (queue_nops(&ring, 1, 0, 25))
+ goto err;
+
+ /* the batch must stop at the skip entry in slot 15 */
+ CHECK_BATCH(&ring, got, cqes, 16, 4);
+ for (i = 0; i < 4; i++) {
+ if (check_cqe(cqes[i], i + 20, 0))
+ goto err;
+ }
+ advance_cqes(&ring, cqes, got);
+
+ /* skip entry now at the head, must be consumed and not returned */
+ CHECK_BATCH(&ring, got, cqes, 16, 2);
+ if (check_cqe(cqes[0], 24, 1))
+ goto err;
+ if (check_cqe(cqes[1], 25, 0))
+ goto err;
+ advance_cqes(&ring, cqes, got);
+
+ CHECK_BATCH(&ring, got, cqes, 16, 0);
+ if (io_uring_cq_ready(&ring)) {
+ fprintf(stderr, "CQ ring not empty\n");
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/cq-peek-batch-mixed.t/ya.make b/contrib/libs/liburing/test/cq-peek-batch-mixed.t/ya.make
new file mode 100644
index 00000000000..58f99535c85
--- /dev/null
+++ b/contrib/libs/liburing/test/cq-peek-batch-mixed.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ cq-peek-batch-mixed.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/cq-peek-batch.t/ya.make b/contrib/libs/liburing/test/cq-peek-batch.t/ya.make
index 1d2c5e7bf06..f803d9f0482 100644
--- a/contrib/libs/liburing/test/cq-peek-batch.t/ya.make
+++ b/contrib/libs/liburing/test/cq-peek-batch.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/cq-ready.t/ya.make b/contrib/libs/liburing/test/cq-ready.t/ya.make
index 6d618b097e1..6e508ddc6a9 100644
--- a/contrib/libs/liburing/test/cq-ready.t/ya.make
+++ b/contrib/libs/liburing/test/cq-ready.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/cq-size.t/ya.make b/contrib/libs/liburing/test/cq-size.t/ya.make
index 55553f1199c..0a87f218a1b 100644
--- a/contrib/libs/liburing/test/cq-size.t/ya.make
+++ b/contrib/libs/liburing/test/cq-size.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/d4ae271dfaae.t/ya.make b/contrib/libs/liburing/test/d4ae271dfaae.t/ya.make
index 53acfab8b83..44754839030 100644
--- a/contrib/libs/liburing/test/d4ae271dfaae.t/ya.make
+++ b/contrib/libs/liburing/test/d4ae271dfaae.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/d77a67ed5f27.t/ya.make b/contrib/libs/liburing/test/d77a67ed5f27.t/ya.make
index f4fba31c91e..90c868ed949 100644
--- a/contrib/libs/liburing/test/d77a67ed5f27.t/ya.make
+++ b/contrib/libs/liburing/test/d77a67ed5f27.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/defer-taskrun.t/ya.make b/contrib/libs/liburing/test/defer-taskrun.t/ya.make
index 4b1712fbb88..1df4cee88fe 100644
--- a/contrib/libs/liburing/test/defer-taskrun.t/ya.make
+++ b/contrib/libs/liburing/test/defer-taskrun.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/defer-tw-timeout.t/ya.make b/contrib/libs/liburing/test/defer-tw-timeout.t/ya.make
index 16ef8aa7010..38836e8f586 100644
--- a/contrib/libs/liburing/test/defer-tw-timeout.t/ya.make
+++ b/contrib/libs/liburing/test/defer-tw-timeout.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/defer.t/ya.make b/contrib/libs/liburing/test/defer.t/ya.make
index 96c3228e2a3..31e12ae7d47 100644
--- a/contrib/libs/liburing/test/defer.t/ya.make
+++ b/contrib/libs/liburing/test/defer.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/double-poll-crash.t/ya.make b/contrib/libs/liburing/test/double-poll-crash.t/ya.make
index b31803d1cd5..afe8881c266 100644
--- a/contrib/libs/liburing/test/double-poll-crash.t/ya.make
+++ b/contrib/libs/liburing/test/double-poll-crash.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/drop-submit.t/ya.make b/contrib/libs/liburing/test/drop-submit.t/ya.make
index d088c970049..30136c0d189 100644
--- a/contrib/libs/liburing/test/drop-submit.t/ya.make
+++ b/contrib/libs/liburing/test/drop-submit.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/eeed8b54e0df.t/ya.make b/contrib/libs/liburing/test/eeed8b54e0df.t/ya.make
index bfddf449d9f..c77753cad74 100644
--- a/contrib/libs/liburing/test/eeed8b54e0df.t/ya.make
+++ b/contrib/libs/liburing/test/eeed8b54e0df.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/empty-eownerdead.t/ya.make b/contrib/libs/liburing/test/empty-eownerdead.t/ya.make
index 889c61976d4..d466cc466f7 100644
--- a/contrib/libs/liburing/test/empty-eownerdead.t/ya.make
+++ b/contrib/libs/liburing/test/empty-eownerdead.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/eploop.t/ya.make b/contrib/libs/liburing/test/eploop.t/ya.make
index b090dea2261..8f5f6d3a1df 100644
--- a/contrib/libs/liburing/test/eploop.t/ya.make
+++ b/contrib/libs/liburing/test/eploop.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/epwait.t/ya.make b/contrib/libs/liburing/test/epwait.t/ya.make
index e588c6505f4..ea0cfad71a6 100644
--- a/contrib/libs/liburing/test/epwait.t/ya.make
+++ b/contrib/libs/liburing/test/epwait.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/eventfd-disable.t/ya.make b/contrib/libs/liburing/test/eventfd-disable.t/ya.make
index 0132e05f32a..20809689685 100644
--- a/contrib/libs/liburing/test/eventfd-disable.t/ya.make
+++ b/contrib/libs/liburing/test/eventfd-disable.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/eventfd-reg.t/ya.make b/contrib/libs/liburing/test/eventfd-reg.t/ya.make
index 600a2e718bd..3e83aba7977 100644
--- a/contrib/libs/liburing/test/eventfd-reg.t/ya.make
+++ b/contrib/libs/liburing/test/eventfd-reg.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/eventfd-ring.c b/contrib/libs/liburing/test/eventfd-ring.c
index 15b4364f54f..d774edbaef7 100644
--- a/contrib/libs/liburing/test/eventfd-ring.c
+++ b/contrib/libs/liburing/test/eventfd-ring.c
@@ -26,6 +26,8 @@ static int test(int flags)
p.flags = flags;
ret = io_uring_queue_init_params(8, &ring1, &p);
if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
fprintf(stderr, "ring setup failed: %d\n", ret);
return T_EXIT_FAIL;
}
@@ -113,9 +115,7 @@ int main(int argc, char *argv[])
}
ret = test(IORING_SETUP_DEFER_TASKRUN|IORING_SETUP_SINGLE_ISSUER);
- if (ret == T_EXIT_SKIP) {
- return T_EXIT_SKIP;
- } else if (ret != T_EXIT_PASS) {
+ if (ret == T_EXIT_FAIL) {
fprintf(stderr, "test defer failed\n");
return T_EXIT_FAIL;
}
diff --git a/contrib/libs/liburing/test/eventfd-ring.t/ya.make b/contrib/libs/liburing/test/eventfd-ring.t/ya.make
index a5e24e02304..713e48d59aa 100644
--- a/contrib/libs/liburing/test/eventfd-ring.t/ya.make
+++ b/contrib/libs/liburing/test/eventfd-ring.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/eventfd.t/ya.make b/contrib/libs/liburing/test/eventfd.t/ya.make
index a8f40e222fa..ee7561c4546 100644
--- a/contrib/libs/liburing/test/eventfd.t/ya.make
+++ b/contrib/libs/liburing/test/eventfd.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/evfd-short-read.t/ya.make b/contrib/libs/liburing/test/evfd-short-read.t/ya.make
index bac1391a733..7e3c33d4c26 100644
--- a/contrib/libs/liburing/test/evfd-short-read.t/ya.make
+++ b/contrib/libs/liburing/test/evfd-short-read.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/evloop.t/ya.make b/contrib/libs/liburing/test/evloop.t/ya.make
index bdd68b1f120..94f45445b73 100644
--- a/contrib/libs/liburing/test/evloop.t/ya.make
+++ b/contrib/libs/liburing/test/evloop.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/exec-target.t/ya.make b/contrib/libs/liburing/test/exec-target.t/ya.make
index 5a07c21cfc6..9eadde7ce61 100644
--- a/contrib/libs/liburing/test/exec-target.t/ya.make
+++ b/contrib/libs/liburing/test/exec-target.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/exit-no-cleanup.t/ya.make b/contrib/libs/liburing/test/exit-no-cleanup.t/ya.make
index 8917ec1e525..df14a734923 100644
--- a/contrib/libs/liburing/test/exit-no-cleanup.t/ya.make
+++ b/contrib/libs/liburing/test/exit-no-cleanup.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fadvise.t/ya.make b/contrib/libs/liburing/test/fadvise.t/ya.make
index 1da8ca007c0..4ca0b02f3da 100644
--- a/contrib/libs/liburing/test/fadvise.t/ya.make
+++ b/contrib/libs/liburing/test/fadvise.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fallocate.c b/contrib/libs/liburing/test/fallocate.c
index 3ef155ee776..53a2bfd6815 100644
--- a/contrib/libs/liburing/test/fallocate.c
+++ b/contrib/libs/liburing/test/fallocate.c
@@ -117,7 +117,7 @@ static int test_fallocate(struct io_uring *ring)
goto err;
}
- if (cqe->res == -EINVAL) {
+ if (cqe->res == -EINVAL || cqe->res == -EOPNOTSUPP) {
fprintf(stdout, "Fallocate not supported, skipping\n");
no_fallocate = 1;
goto skip;
diff --git a/contrib/libs/liburing/test/fallocate.t/ya.make b/contrib/libs/liburing/test/fallocate.t/ya.make
index 67a54e6b260..e656fed96e0 100644
--- a/contrib/libs/liburing/test/fallocate.t/ya.make
+++ b/contrib/libs/liburing/test/fallocate.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fc2a85cb02ef.t/ya.make b/contrib/libs/liburing/test/fc2a85cb02ef.t/ya.make
index 7fb9413a120..0041e627810 100644
--- a/contrib/libs/liburing/test/fc2a85cb02ef.t/ya.make
+++ b/contrib/libs/liburing/test/fc2a85cb02ef.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fd-install.t/ya.make b/contrib/libs/liburing/test/fd-install.t/ya.make
index e3931dd1396..c9a1be473e1 100644
--- a/contrib/libs/liburing/test/fd-install.t/ya.make
+++ b/contrib/libs/liburing/test/fd-install.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fd-pass.c b/contrib/libs/liburing/test/fd-pass.c
index 651d3a885b5..4c074383cc6 100644
--- a/contrib/libs/liburing/test/fd-pass.c
+++ b/contrib/libs/liburing/test/fd-pass.c
@@ -61,6 +61,8 @@ static int test(const char *filename, int source_fd, int target_fd,
ret = io_uring_queue_init(8, &sring, ring_flags);
if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
fprintf(stderr, "ring setup failed: %d\n", ret);
return T_EXIT_FAIL;
}
@@ -238,6 +240,8 @@ int main(int argc, char *argv[])
if (ret == T_EXIT_FAIL) {
fprintf(stderr, "test failed 0 2 defer\n");
ret = T_EXIT_FAIL;
+ } else if (ret == T_EXIT_SKIP) {
+ return T_EXIT_PASS;
}
ret = test(fname, 1, 1, IORING_SETUP_DEFER_TASKRUN|IORING_SETUP_SINGLE_ISSUER);
diff --git a/contrib/libs/liburing/test/fd-pass.t/ya.make b/contrib/libs/liburing/test/fd-pass.t/ya.make
index 5355c378816..6aadfd81a72 100644
--- a/contrib/libs/liburing/test/fd-pass.t/ya.make
+++ b/contrib/libs/liburing/test/fd-pass.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fdinfo-sqpoll.t/ya.make b/contrib/libs/liburing/test/fdinfo-sqpoll.t/ya.make
index 69e8dffd674..70428d6627d 100644
--- a/contrib/libs/liburing/test/fdinfo-sqpoll.t/ya.make
+++ b/contrib/libs/liburing/test/fdinfo-sqpoll.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fdinfo.c b/contrib/libs/liburing/test/fdinfo.c
index 7a5edc495ec..f011fc113a8 100644
--- a/contrib/libs/liburing/test/fdinfo.c
+++ b/contrib/libs/liburing/test/fdinfo.c
@@ -183,13 +183,19 @@ static int __test_io(const char *file, struct io_uring *ring, int write,
fdinfo_read(ring);
ret = io_uring_submit(ring);
- if (!mixed && ret != BUFFERS) {
- fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
- goto err;
- } else if (mixed && ret != BUFFERS + (BUFFERS >> 1)) {
- fprintf(stderr, "submit got %d, wanted %d\n", ret,
- BUFFERS + (BUFFERS >> 1));
- goto err;
+ if (!sqthread) {
+ if (ret < 0) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto err;
+ }
+ if (!mixed && ret != BUFFERS) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
+ goto err;
+ } else if (mixed && ret != BUFFERS + (BUFFERS >> 1)) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret,
+ BUFFERS + (BUFFERS >> 1));
+ goto err;
+ }
}
for (i = 0; i < 10; i++) {
diff --git a/contrib/libs/liburing/test/fdinfo.t/ya.make b/contrib/libs/liburing/test/fdinfo.t/ya.make
index 70eed309420..b9cc697f34f 100644
--- a/contrib/libs/liburing/test/fdinfo.t/ya.make
+++ b/contrib/libs/liburing/test/fdinfo.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fifo-futex-poll.t/ya.make b/contrib/libs/liburing/test/fifo-futex-poll.t/ya.make
index f8dc3993ceb..41ba73164ad 100644
--- a/contrib/libs/liburing/test/fifo-futex-poll.t/ya.make
+++ b/contrib/libs/liburing/test/fifo-futex-poll.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fifo-nonblock-read.t/ya.make b/contrib/libs/liburing/test/fifo-nonblock-read.t/ya.make
index a0eb171836f..aab565db6c4 100644
--- a/contrib/libs/liburing/test/fifo-nonblock-read.t/ya.make
+++ b/contrib/libs/liburing/test/fifo-nonblock-read.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/file-alloc-range-hint.c b/contrib/libs/liburing/test/file-alloc-range-hint.c
new file mode 100644
index 00000000000..264a6422ce5
--- /dev/null
+++ b/contrib/libs/liburing/test/file-alloc-range-hint.c
@@ -0,0 +1,219 @@
+#include "../config-host.h"
+// SPDX-License-Identifier: MIT
+/*
+ * Test that auto-allocated fixed file slots stay within the configured
+ * alloc range, even after explicit operations on slots outside the range
+ * corrupt alloc_hint.
+ *
+ * Bug: io_file_bitmap_clear() and io_file_bitmap_set() unconditionally
+ * update alloc_hint to the bit position. If a file is installed/removed
+ * at a slot outside the configured alloc range, alloc_hint can point
+ * outside the range, causing subsequent auto-allocations to escape it.
+ */
+#include <stdio.h>
+#include <unistd.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int no_sparse;
+
+/*
+ * Auto-alloc a file into the fixed table using IORING_FILE_INDEX_ALLOC.
+ * On success, the kernel writes back the allocated slot index into *fd
+ * (via put_user). Returns cqe->res (count on success, negative on error).
+ */
+static int file_update_alloc(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_files_update(sqe, fd, 1, IORING_FILE_INDEX_ALLOC);
+
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ return -1;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+/*
+ * Install a file at a slot below the alloc range, then remove it.
+ * This corrupts alloc_hint to point below the range. Verify that
+ * subsequent auto-alloc still returns a slot within the range.
+ */
+static int test_hint_below_range(void)
+{
+ struct io_uring ring;
+ int pipe_fds[2];
+ int roff = 10, rlen = 10;
+ int ret, fd;
+
+ if (pipe(pipe_fds)) {
+ fprintf(stderr, "pipe\n");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_register_files_sparse(&ring, 20);
+ if (ret) {
+ if (ret == -EINVAL) {
+ no_sparse = 1;
+ goto err;
+ }
+ fprintf(stderr, "register_files_sparse: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_register_file_alloc_range(&ring, roff, rlen);
+ if (ret) {
+ fprintf(stderr, "register_file_alloc_range: %d\n", ret);
+ goto err;
+ }
+
+ /* Install a file at slot 2, outside the alloc range [10, 20) */
+ fd = pipe_fds[0];
+ ret = io_uring_register_files_update(&ring, 2, &fd, 1);
+ if (ret != 1) {
+ fprintf(stderr, "install at slot 2: %d\n", ret);
+ goto err;
+ }
+
+ /* Remove it — corrupts alloc_hint to point at slot 2 */
+ fd = -1;
+ ret = io_uring_register_files_update(&ring, 2, &fd, 1);
+ if (ret != 1) {
+ fprintf(stderr, "remove at slot 2: %d\n", ret);
+ goto err;
+ }
+
+ /* Auto-alloc should stay within [10, 20), not land at slot 2 */
+ fd = pipe_fds[1];
+ ret = file_update_alloc(&ring, &fd);
+ if (ret != 1) {
+ fprintf(stderr, "file_update_alloc: %d\n", ret);
+ goto err;
+ }
+
+ if (fd < roff || fd >= roff + rlen) {
+ fprintf(stderr, "alloc returned slot %d, expected [%d, %d)\n",
+ fd, roff, roff + rlen);
+ goto err;
+ }
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return no_sparse ? T_EXIT_SKIP : T_EXIT_FAIL;
+}
+
+/*
+ * Install a file at a slot above the alloc range end. The bitmap_set
+ * pushes alloc_hint above file_alloc_end. Verify auto-alloc still
+ * stays within range.
+ */
+static int test_hint_above_range(void)
+{
+ struct io_uring ring;
+ int pipe_fds[2];
+ int roff = 0, rlen = 10;
+ int ret, fd;
+
+ if (pipe(pipe_fds)) {
+ fprintf(stderr, "pipe\n");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_register_files_sparse(&ring, 20);
+ if (ret) {
+ fprintf(stderr, "register_files_sparse: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_register_file_alloc_range(&ring, roff, rlen);
+ if (ret) {
+ fprintf(stderr, "register_file_alloc_range: %d\n", ret);
+ goto err;
+ }
+
+ /* Install at slot 15, above range [0, 10) — alloc_hint = 16 */
+ fd = pipe_fds[0];
+ ret = io_uring_register_files_update(&ring, 15, &fd, 1);
+ if (ret != 1) {
+ fprintf(stderr, "install at slot 15: %d\n", ret);
+ goto err;
+ }
+
+ /* Auto-alloc should stay within [0, 10), not search [0, 16) */
+ fd = pipe_fds[1];
+ ret = file_update_alloc(&ring, &fd);
+ if (ret != 1) {
+ fprintf(stderr, "file_update_alloc: %d\n", ret);
+ goto err;
+ }
+
+ if (fd < roff || fd >= roff + rlen) {
+ fprintf(stderr, "alloc returned slot %d, expected [%d, %d)\n",
+ fd, roff, roff + rlen);
+ goto err;
+ }
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_hint_below_range();
+ if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ } else if (ret) {
+ fprintf(stderr, "test_hint_below_range failed\n");
+ return T_EXIT_FAIL;
+ }
+ if (no_sparse)
+ return T_EXIT_SKIP;
+
+ ret = test_hint_above_range();
+ if (ret) {
+ fprintf(stderr, "test_hint_above_range failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/file-alloc-range-hint.t/ya.make b/contrib/libs/liburing/test/file-alloc-range-hint.t/ya.make
new file mode 100644
index 00000000000..355969cf2ae
--- /dev/null
+++ b/contrib/libs/liburing/test/file-alloc-range-hint.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ file-alloc-range-hint.c
+ helpers.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/file-exit-unreg.t/ya.make b/contrib/libs/liburing/test/file-exit-unreg.t/ya.make
index b95e1a2d024..a09cd274dbc 100644
--- a/contrib/libs/liburing/test/file-exit-unreg.t/ya.make
+++ b/contrib/libs/liburing/test/file-exit-unreg.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/file-register.t/ya.make b/contrib/libs/liburing/test/file-register.t/ya.make
index eca27581265..bc59e0b6901 100644
--- a/contrib/libs/liburing/test/file-register.t/ya.make
+++ b/contrib/libs/liburing/test/file-register.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/file-update.t/ya.make b/contrib/libs/liburing/test/file-update.t/ya.make
index bdb7f261964..6f84a8aaffb 100644
--- a/contrib/libs/liburing/test/file-update.t/ya.make
+++ b/contrib/libs/liburing/test/file-update.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/file-verify.t/ya.make b/contrib/libs/liburing/test/file-verify.t/ya.make
index fed39d06b2f..bb79e3b46ce 100644
--- a/contrib/libs/liburing/test/file-verify.t/ya.make
+++ b/contrib/libs/liburing/test/file-verify.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/files-exit-hang-poll.t/ya.make b/contrib/libs/liburing/test/files-exit-hang-poll.t/ya.make
index 5c608680b37..860a4bde920 100644
--- a/contrib/libs/liburing/test/files-exit-hang-poll.t/ya.make
+++ b/contrib/libs/liburing/test/files-exit-hang-poll.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/files-exit-hang-timeout.t/ya.make b/contrib/libs/liburing/test/files-exit-hang-timeout.t/ya.make
index 48b4e238659..81e1d1e694d 100644
--- a/contrib/libs/liburing/test/files-exit-hang-timeout.t/ya.make
+++ b/contrib/libs/liburing/test/files-exit-hang-timeout.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fixed-buf-iter.t/ya.make b/contrib/libs/liburing/test/fixed-buf-iter.t/ya.make
index 55b94a8364e..b6143729fb9 100644
--- a/contrib/libs/liburing/test/fixed-buf-iter.t/ya.make
+++ b/contrib/libs/liburing/test/fixed-buf-iter.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fixed-buf-merge.t/ya.make b/contrib/libs/liburing/test/fixed-buf-merge.t/ya.make
index c6c5dba00fd..431601b1772 100644
--- a/contrib/libs/liburing/test/fixed-buf-merge.t/ya.make
+++ b/contrib/libs/liburing/test/fixed-buf-merge.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fixed-hugepage.t/ya.make b/contrib/libs/liburing/test/fixed-hugepage.t/ya.make
index 6bb7512170f..bf92ae022eb 100644
--- a/contrib/libs/liburing/test/fixed-hugepage.t/ya.make
+++ b/contrib/libs/liburing/test/fixed-hugepage.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fixed-link.t/ya.make b/contrib/libs/liburing/test/fixed-link.t/ya.make
index 3b2fcb43240..bb6b9fce0f7 100644
--- a/contrib/libs/liburing/test/fixed-link.t/ya.make
+++ b/contrib/libs/liburing/test/fixed-link.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fixed-reuse.t/ya.make b/contrib/libs/liburing/test/fixed-reuse.t/ya.make
index 1d2698d652b..54009f7adda 100644
--- a/contrib/libs/liburing/test/fixed-reuse.t/ya.make
+++ b/contrib/libs/liburing/test/fixed-reuse.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fixed-seg.t/ya.make b/contrib/libs/liburing/test/fixed-seg.t/ya.make
index 21c12796c83..50fdc70db99 100644
--- a/contrib/libs/liburing/test/fixed-seg.t/ya.make
+++ b/contrib/libs/liburing/test/fixed-seg.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fpos.t/ya.make b/contrib/libs/liburing/test/fpos.t/ya.make
index c391bf80094..7c1a5f05e33 100644
--- a/contrib/libs/liburing/test/fpos.t/ya.make
+++ b/contrib/libs/liburing/test/fpos.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fsnotify.t/ya.make b/contrib/libs/liburing/test/fsnotify.t/ya.make
index a7b895fc43d..080de33db6b 100644
--- a/contrib/libs/liburing/test/fsnotify.t/ya.make
+++ b/contrib/libs/liburing/test/fsnotify.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/fsync.t/ya.make b/contrib/libs/liburing/test/fsync.t/ya.make
index eaa1eee333f..6b8962b017e 100644
--- a/contrib/libs/liburing/test/fsync.t/ya.make
+++ b/contrib/libs/liburing/test/fsync.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/futex-kill.t/ya.make b/contrib/libs/liburing/test/futex-kill.t/ya.make
index d190bbf6be1..62205f896a4 100644
--- a/contrib/libs/liburing/test/futex-kill.t/ya.make
+++ b/contrib/libs/liburing/test/futex-kill.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/futex.t/ya.make b/contrib/libs/liburing/test/futex.t/ya.make
index 38481d2b7c0..65468493285 100644
--- a/contrib/libs/liburing/test/futex.t/ya.make
+++ b/contrib/libs/liburing/test/futex.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/hardlink.t/ya.make b/contrib/libs/liburing/test/hardlink.t/ya.make
index 5d888cbad9f..d9c684a2324 100644
--- a/contrib/libs/liburing/test/hardlink.t/ya.make
+++ b/contrib/libs/liburing/test/hardlink.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/ignore-single-mmap.t/ya.make b/contrib/libs/liburing/test/ignore-single-mmap.t/ya.make
index e5ebe7d2968..bd46f7f46c1 100644
--- a/contrib/libs/liburing/test/ignore-single-mmap.t/ya.make
+++ b/contrib/libs/liburing/test/ignore-single-mmap.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/init-mem.t/ya.make b/contrib/libs/liburing/test/init-mem.t/ya.make
index 97ef790675f..3cdf73fe916 100644
--- a/contrib/libs/liburing/test/init-mem.t/ya.make
+++ b/contrib/libs/liburing/test/init-mem.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/io-cancel.t/ya.make b/contrib/libs/liburing/test/io-cancel.t/ya.make
index 5df0f015926..3b70e765252 100644
--- a/contrib/libs/liburing/test/io-cancel.t/ya.make
+++ b/contrib/libs/liburing/test/io-cancel.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/io-wq-exit.t/ya.make b/contrib/libs/liburing/test/io-wq-exit.t/ya.make
index 5d7b9365c86..f02679eec3e 100644
--- a/contrib/libs/liburing/test/io-wq-exit.t/ya.make
+++ b/contrib/libs/liburing/test/io-wq-exit.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/io-wq-unused-exit.t/ya.make b/contrib/libs/liburing/test/io-wq-unused-exit.t/ya.make
index a9ab64699c7..d8429afee5a 100644
--- a/contrib/libs/liburing/test/io-wq-unused-exit.t/ya.make
+++ b/contrib/libs/liburing/test/io-wq-unused-exit.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/io_uring_enter.t/ya.make b/contrib/libs/liburing/test/io_uring_enter.t/ya.make
index ddd25e7c970..7a7fecdbe71 100644
--- a/contrib/libs/liburing/test/io_uring_enter.t/ya.make
+++ b/contrib/libs/liburing/test/io_uring_enter.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/io_uring_passthrough.c b/contrib/libs/liburing/test/io_uring_passthrough.c
index b405601ec43..314908fd505 100644
--- a/contrib/libs/liburing/test/io_uring_passthrough.c
+++ b/contrib/libs/liburing/test/io_uring_passthrough.c
@@ -282,13 +282,11 @@ static int test_io(const char *file, int tc, int read, int sqthread,
return 0;
ret = t_create_ring(128, &ring, ring_flags);
- if (ret == T_SETUP_SKIP)
- return 0;
+ if (ret == T_SETUP_SKIP) {
+ no_pt = 1;
+ return T_SETUP_SKIP;
+ }
if (ret != T_SETUP_OK) {
- if (ret == -EINVAL) {
- no_pt = 1;
- return T_SETUP_SKIP;
- }
fprintf(stderr, "ring create failed: %d\n", ret);
return 1;
}
@@ -381,6 +379,8 @@ static int test_io_uring_submit_enters(const char *file)
ret = io_uring_queue_init(64, &ring, ring_flags);
if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
fprintf(stderr, "ring create failed: %d\n", ret);
return 1;
}
@@ -499,7 +499,7 @@ int main(int argc, char *argv[])
return T_EXIT_SKIP;
ret = test_io_uring_submit_enters(fname);
- if (ret) {
+ if (ret == T_EXIT_FAIL) {
fprintf(stderr, "test_io_uring_submit_enters failed\n");
goto err;
}
diff --git a/contrib/libs/liburing/test/io_uring_passthrough.t/ya.make b/contrib/libs/liburing/test/io_uring_passthrough.t/ya.make
index af527cca9e7..b8dc8e3cef7 100644
--- a/contrib/libs/liburing/test/io_uring_passthrough.t/ya.make
+++ b/contrib/libs/liburing/test/io_uring_passthrough.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/io_uring_register.t/ya.make b/contrib/libs/liburing/test/io_uring_register.t/ya.make
index 884fe0daab3..eb66943eca6 100644
--- a/contrib/libs/liburing/test/io_uring_register.t/ya.make
+++ b/contrib/libs/liburing/test/io_uring_register.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/io_uring_setup.t/ya.make b/contrib/libs/liburing/test/io_uring_setup.t/ya.make
index 4113d9e40f9..ec6d58c7956 100644
--- a/contrib/libs/liburing/test/io_uring_setup.t/ya.make
+++ b/contrib/libs/liburing/test/io_uring_setup.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/iopoll-leak.t/ya.make b/contrib/libs/liburing/test/iopoll-leak.t/ya.make
index 96905deeedf..d77ce0b3209 100644
--- a/contrib/libs/liburing/test/iopoll-leak.t/ya.make
+++ b/contrib/libs/liburing/test/iopoll-leak.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/iopoll-overflow.t/ya.make b/contrib/libs/liburing/test/iopoll-overflow.t/ya.make
index 0328057d8a2..dc9985dc139 100644
--- a/contrib/libs/liburing/test/iopoll-overflow.t/ya.make
+++ b/contrib/libs/liburing/test/iopoll-overflow.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/iopoll-sync.c b/contrib/libs/liburing/test/iopoll-sync.c
new file mode 100644
index 00000000000..b6c2832553e
--- /dev/null
+++ b/contrib/libs/liburing/test/iopoll-sync.c
@@ -0,0 +1,128 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test uring_cmds for files that don't support iopoll
+ * on IORING_SETUP_IOPOLL rings
+ */
+
+#include <liburing.h>
+#include <stdio.h>
+#include <sys/socket.h>
+
+#include "helpers.h"
+
+int main(void)
+{
+ int sockfd;
+ int level = SOL_SOCKET;
+ int optname = SO_REUSEADDR;
+ int optval1, optval2, optval3;
+ struct io_uring ring;
+ int ret;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe_iter cqe_iter;
+ struct io_uring_cqe *cqe;
+
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ fprintf(stderr, "socket() failed: %m\n");
+ return T_EXIT_SKIP;
+ }
+
+ optval1 = 0;
+ if (setsockopt(sockfd, level, optname, &optval1, sizeof(optval1)) < 0) {
+ fprintf(stderr, "setsockopt() failed: %m\n");
+ return T_EXIT_SKIP;
+ }
+
+ ret = t_create_ring(3, &ring, IORING_SETUP_IOPOLL);
+ if (ret == T_SETUP_SKIP) {
+ fprintf(stderr, "IORING_SETUP_IOPOLL not supported\n");
+ return T_EXIT_SKIP;
+ }
+ if (ret)
+ return T_EXIT_FAIL;
+
+ optval1 = 123;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_GETSOCKOPT, sockfd,
+ level, optname, &optval1, sizeof(optval1));
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ optval2 = 1;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_SETSOCKOPT, sockfd,
+ level, optname, &optval2, sizeof(optval2));
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ optval3 = 123;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_GETSOCKOPT, sockfd,
+ level, optname, &optval3, sizeof(optval3));
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 3) {
+ fprintf(stderr, "io_uring_submit() returned %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ cqe_iter = io_uring_cqe_iter_init(&ring);
+ if (!io_uring_cqe_iter_next(&cqe_iter, &cqe)) {
+ fprintf(stderr, "No CQE available\n");
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data != 1) {
+ fprintf(stderr, "No CQE for user_data 1\n");
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res == -EOPNOTSUPP || cqe->res == -EINVAL)
+ return T_EXIT_SKIP;
+ if (cqe->res != sizeof(optval1)) {
+ fprintf(stderr, "GETSOCKOPT returned %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ if (optval1 != 0) {
+ fprintf(stderr, "optval %d != 0\n", optval1);
+ return T_EXIT_FAIL;
+ }
+
+ if (!io_uring_cqe_iter_next(&cqe_iter, &cqe)) {
+ fprintf(stderr, "Only 1 CQE available\n");
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data != 2) {
+ fprintf(stderr, "No CQE for user_data 2\n");
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res) {
+ fprintf(stderr, "SETSOCKOPT returned %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+
+ if (!io_uring_cqe_iter_next(&cqe_iter, &cqe)) {
+ fprintf(stderr, "Only 2 CQEs available\n");
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data != 3) {
+ fprintf(stderr, "No CQE for user_data 3\n");
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res != sizeof(optval3)) {
+ fprintf(stderr, "GETSOCKOPT returned %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ if (optval3 != 1) {
+ fprintf(stderr, "optval %d != 1\n", optval3);
+ return T_EXIT_FAIL;
+ }
+
+ if (io_uring_cqe_iter_next(&cqe_iter, &cqe)) {
+ fprintf(stderr, "More than 3 CQEs available");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/iopoll-sync.t/ya.make b/contrib/libs/liburing/test/iopoll-sync.t/ya.make
new file mode 100644
index 00000000000..13e919fec6d
--- /dev/null
+++ b/contrib/libs/liburing/test/iopoll-sync.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ iopoll-sync.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/iopoll.t/ya.make b/contrib/libs/liburing/test/iopoll.t/ya.make
index 56f9e38a904..e060746dba4 100644
--- a/contrib/libs/liburing/test/iopoll.t/ya.make
+++ b/contrib/libs/liburing/test/iopoll.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/iowait.t/ya.make b/contrib/libs/liburing/test/iowait.t/ya.make
index 40e76ce8813..8976b6e12de 100644
--- a/contrib/libs/liburing/test/iowait.t/ya.make
+++ b/contrib/libs/liburing/test/iowait.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/kallsyms.t/ya.make b/contrib/libs/liburing/test/kallsyms.t/ya.make
index d4893a894e0..9cc2e3a1278 100644
--- a/contrib/libs/liburing/test/kallsyms.t/ya.make
+++ b/contrib/libs/liburing/test/kallsyms.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/large-resize.c b/contrib/libs/liburing/test/large-resize.c
new file mode 100644
index 00000000000..0bf510b0a20
--- /dev/null
+++ b/contrib/libs/liburing/test/large-resize.c
@@ -0,0 +1,474 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test ring resizing with fixed large sqes/cqes, and with
+ * mixed mode sqes/cqes.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int test_cqe32_resize(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { }, new_p = { };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ p.flags = IORING_SETUP_CQE32 | IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_SINGLE_ISSUER;
+ p.sq_entries = 8;
+ p.cq_entries = 8;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 4; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 0xAAAA0000ULL + i;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != 4) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Resize the ring */
+ new_p.sq_entries = 8;
+ new_p.cq_entries = 16;
+ new_p.flags = IORING_SETUP_CQSIZE;
+
+ ret = io_uring_resize_rings(&ring, &new_p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "resize failed: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 4; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->user_data != (0xAAAA0000ULL + i)) {
+ fprintf(stderr, " *** CQE32 CORRUPTION DETECTED ***\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
+
+static int test_cqe_mixed_resize(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { }, new_p = { };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ /* Setup ring with CQE_MIXED, DEFER_TASKRUN, SINGLE_ISSUER */
+ p.flags = IORING_SETUP_CQE_MIXED | IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_SINGLE_ISSUER;
+ p.sq_entries = 8;
+ p.cq_entries = 8;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "ring_init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 4; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get_sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (i % 2 == 0) {
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 0xBBBB0000ULL + i;
+ } else {
+ io_uring_prep_nop(sqe); /* Still NOP but different pattern */
+ sqe->user_data = 0xCCCC0000ULL + i;
+ }
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != 4) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Resize the ring */
+ new_p.sq_entries = 8;
+ new_p.cq_entries = 16;
+ new_p.flags = IORING_SETUP_CQSIZE;
+
+ ret = io_uring_resize_rings(&ring, &new_p);
+ if (ret) {
+ fprintf(stderr, "resize failed: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ /* Reap and verify CQEs */
+ for (i = 0; i < 4; i++) {
+ uint64_t expected;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ expected = (i % 2 == 0) ? (0xBBBB0000ULL + i) : (0xCCCC0000ULL + i);
+ if (cqe->user_data != expected) {
+ fprintf(stderr, " *** CQE_MIXED CORRUPTION DETECTED ***\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
+
+static int test_ring_wrapping(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { }, new_p = { };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i, consumed_count;
+ int remaining_expected[] = {6, 7};
+ int remaining_count = 0;
+
+ /* Setup small ring to force wrapping */
+ p.flags = IORING_SETUP_CQE32 | IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_SINGLE_ISSUER;
+ p.sq_entries = 4;
+ p.cq_entries = 4;
+
+ ret = io_uring_queue_init_params(4, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ /* Submit more entries to force head/tail wrapping */
+ consumed_count = 0;
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get_sqe failed at %d\n", i);
+ return T_EXIT_FAIL;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 0xDDDD0000ULL + i;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Consume some to create wrap scenario */
+ if (i >= 2) {
+ uint64_t expected;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ /* Verify early consumed CQE */
+ expected = 0xDDDD0000ULL + consumed_count;
+
+ if (cqe->user_data != expected) {
+ fprintf(stderr, " *** EARLY CQE CORRUPTION DETECTED ***\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ consumed_count++;
+ }
+ }
+
+ /* Now resize with pending wrapped entries */
+ new_p.sq_entries = 4;
+ new_p.cq_entries = 16;
+
+ ret = io_uring_resize_rings(&ring, &new_p);
+ if (ret) {
+ fprintf(stderr, "resize failed: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ /* Consume remaining entries (should be operations 6 and 7) */
+ while (true) {
+ uint64_t expected;
+
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ if (ret == -EAGAIN)
+ break;
+ if (ret) {
+ fprintf(stderr, "peek_cqe: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ if (remaining_count >= 2) {
+ printf(" *** TOO MANY REMAINING CQES ***\n");
+ return T_EXIT_FAIL;
+ }
+
+ expected = 0xDDDD0000ULL + remaining_expected[remaining_count];
+ if (cqe->user_data != expected) {
+ fprintf(stderr, " *** REMAINING CQE CORRUPTION DETECTED ***\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ remaining_count++;
+ }
+
+ if (remaining_count != 2) {
+ fprintf(stderr, " *** WRONG NUMBER OF REMAINING CQES: got %d, expected 2 ***\n", remaining_count);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
+
+static int test_sqe128_resize(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { }, new_p = { };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ p.flags = IORING_SETUP_SQE128 | IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_SINGLE_ISSUER;
+ p.sq_entries = 8;
+ p.cq_entries = 8;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ /* Prepare 4 large SQEs but don't submit them yet */
+ for (i = 0; i < 4; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get_sqe failed\n");
+ return 1;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 0xEEEE0000ULL + i;
+
+ /* Fill some extended SQE data to ensure 128-byte copy works */
+ if (ring.sq.ring_ptr) {
+ /* Access extended part of SQE128 if available */
+ memset(sqe->cmd, 0x55 + i, sizeof(sqe->cmd));
+ }
+ }
+
+ __io_uring_flush_sq(&ring);
+
+ /* Resize the ring while SQEs are pending */
+ new_p.sq_entries = 16;
+ new_p.cq_entries = 16;
+ new_p.flags = IORING_SETUP_CQSIZE;
+
+ ret = io_uring_resize_rings(&ring, &new_p);
+ if (ret) {
+ fprintf(stderr, "resize failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ /* Now submit the pending SQEs */
+ ret = io_uring_submit(&ring);
+ if (ret != 4) {
+ fprintf(stderr, "submit after resize: %d (expected 4)\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Reap and verify CQEs */
+ for (i = 0; i < 4; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->user_data != (0xEEEE0000ULL + i)) {
+ printf(" *** SQE128 CORRUPTION DETECTED ***\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
+
+static int test_sqe_mixed_resize(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { }, new_p = { };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ p.flags = IORING_SETUP_SQE_MIXED | IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_SINGLE_ISSUER;
+ p.sq_entries = 8;
+ p.cq_entries = 8;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ return T_EXIT_FAIL;
+ }
+
+ /* Prepare mix of regular and large SQEs but don't submit them yet */
+ for (i = 0; i < 4; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get_sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ /* Alternate between regular NOPs and operations that might use large SQEs */
+ if (i % 2 == 0) {
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 0xFFFF0000ULL + i;
+ } else {
+ io_uring_prep_nop(sqe); /* Still NOP but different pattern */
+ sqe->user_data = 0x11110000ULL + i;
+ /* Fill extended data for mixed SQE */
+ memset(sqe->cmd, 0xAA + i, sizeof(sqe->cmd));
+ }
+ }
+
+ __io_uring_flush_sq(&ring);
+
+ /* Resize the ring while mixed SQEs are pending */
+ new_p.sq_entries = 16;
+ new_p.cq_entries = 16;
+ new_p.flags = IORING_SETUP_CQSIZE;
+
+ ret = io_uring_resize_rings(&ring, &new_p);
+ if (ret) {
+ fprintf(stderr, "resize failed: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ /* Now submit the pending mixed SQEs */
+ ret = io_uring_submit(&ring);
+ if (ret != 4) {
+ fprintf(stderr, "submit after resize: %d (expected 4)\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Reap and verify CQEs */
+ for (i = 0; i < 4; i++) {
+ uint64_t expected;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %s\n", strerror(-ret));
+ return T_EXIT_FAIL;
+ }
+
+ expected = (i % 2 == 0) ? (0xFFFF0000ULL + i) : (0x11110000ULL + i);
+ if (cqe->user_data != expected) {
+ fprintf(stderr, " *** SQE_MIXED CORRUPTION DETECTED ***\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
+
+int main(void)
+{
+ int ret;
+
+ ret = test_cqe32_resize();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "cqe32_resize failed\n");
+ return T_EXIT_FAIL;
+ } else if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ }
+
+ ret = test_cqe_mixed_resize();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "cqe_mixed_resize failed\n");
+ return T_EXIT_FAIL;
+ } else if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ }
+
+ ret = test_sqe128_resize();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "sqe128_resize failed\n");
+ return T_EXIT_FAIL;
+ } else if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ }
+
+ ret = test_ring_wrapping();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "ring_wrapping failed\n");
+ return T_EXIT_FAIL;
+ } else if (ret == T_EXIT_SKIP) {
+ return T_EXIT_SKIP;
+ }
+
+ ret = test_sqe_mixed_resize();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "sqe_mixed_resize failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/large-resize.t/ya.make b/contrib/libs/liburing/test/large-resize.t/ya.make
new file mode 100644
index 00000000000..c421f257ac1
--- /dev/null
+++ b/contrib/libs/liburing/test/large-resize.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ large-resize.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/lfs-openat-write.t/ya.make b/contrib/libs/liburing/test/lfs-openat-write.t/ya.make
index 6722b0e576e..7e93c7a5608 100644
--- a/contrib/libs/liburing/test/lfs-openat-write.t/ya.make
+++ b/contrib/libs/liburing/test/lfs-openat-write.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/lfs-openat.t/ya.make b/contrib/libs/liburing/test/lfs-openat.t/ya.make
index 98522d42381..24b5cc45d51 100644
--- a/contrib/libs/liburing/test/lfs-openat.t/ya.make
+++ b/contrib/libs/liburing/test/lfs-openat.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/link-timeout.c b/contrib/libs/liburing/test/link-timeout.c
index 4d4f5d4c69b..a727418f889 100644
--- a/contrib/libs/liburing/test/link-timeout.c
+++ b/contrib/libs/liburing/test/link-timeout.c
@@ -18,7 +18,7 @@
static int test_fail_lone_link_timeouts(struct io_uring *ring)
{
- struct __kernel_timespec ts;
+ struct __kernel_timespec ts = { };
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
int ret;
diff --git a/contrib/libs/liburing/test/link-timeout.t/ya.make b/contrib/libs/liburing/test/link-timeout.t/ya.make
index e6c49456c70..5ba0ee918d7 100644
--- a/contrib/libs/liburing/test/link-timeout.t/ya.make
+++ b/contrib/libs/liburing/test/link-timeout.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/link.t/ya.make b/contrib/libs/liburing/test/link.t/ya.make
index 45d69c82bfb..0b78a9f410a 100644
--- a/contrib/libs/liburing/test/link.t/ya.make
+++ b/contrib/libs/liburing/test/link.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/link_drain.t/ya.make b/contrib/libs/liburing/test/link_drain.t/ya.make
index bd3c0513ca6..c1d34d0dc65 100644
--- a/contrib/libs/liburing/test/link_drain.t/ya.make
+++ b/contrib/libs/liburing/test/link_drain.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/linked-defer-close.t/ya.make b/contrib/libs/liburing/test/linked-defer-close.t/ya.make
index a447a5629de..eaeaa0fe34f 100644
--- a/contrib/libs/liburing/test/linked-defer-close.t/ya.make
+++ b/contrib/libs/liburing/test/linked-defer-close.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/madvise.t/ya.make b/contrib/libs/liburing/test/madvise.t/ya.make
index ad8c7b41f13..d29d31e9480 100644
--- a/contrib/libs/liburing/test/madvise.t/ya.make
+++ b/contrib/libs/liburing/test/madvise.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/min-timeout-wait.c b/contrib/libs/liburing/test/min-timeout-wait.c
index 7b690563df9..8fd886da5d1 100644
--- a/contrib/libs/liburing/test/min-timeout-wait.c
+++ b/contrib/libs/liburing/test/min-timeout-wait.c
@@ -204,7 +204,7 @@ static int test_some(struct io_uring *ring)
*/
static int test_already(struct io_uring *ring)
{
- return __test_nop(ring, 4, 0, 1, 100000, __FUNCTION__);
+ return __test_nop(ring, 4, 0, 5, 100000, __FUNCTION__);
}
/*
diff --git a/contrib/libs/liburing/test/min-timeout-wait.t/ya.make b/contrib/libs/liburing/test/min-timeout-wait.t/ya.make
index 19b3931b672..52f69cb8d20 100644
--- a/contrib/libs/liburing/test/min-timeout-wait.t/ya.make
+++ b/contrib/libs/liburing/test/min-timeout-wait.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/min-timeout.c b/contrib/libs/liburing/test/min-timeout.c
index 0803b0cc530..f98a7311308 100644
--- a/contrib/libs/liburing/test/min-timeout.c
+++ b/contrib/libs/liburing/test/min-timeout.c
@@ -146,6 +146,7 @@ static int test(int flags, int expected_ctx, int min_wait, int write_delay,
close(fds[i][1]);
}
+ io_uring_queue_exit(&ring);
return T_EXIT_PASS;
}
@@ -155,7 +156,6 @@ int main(int argc, char *argv[])
if (argc > 1)
return T_EXIT_SKIP;
-
ret = test(0, NWRITES + 1, 0, 2000, NWRITES, WAIT_USEC / 1000);
if (ret == T_EXIT_FAIL)
return T_EXIT_FAIL;
diff --git a/contrib/libs/liburing/test/min-timeout.t/ya.make b/contrib/libs/liburing/test/min-timeout.t/ya.make
index c84e21cd99e..28908658755 100644
--- a/contrib/libs/liburing/test/min-timeout.t/ya.make
+++ b/contrib/libs/liburing/test/min-timeout.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/mkdir.t/ya.make b/contrib/libs/liburing/test/mkdir.t/ya.make
index eecdc0a2b53..fd23fc73e33 100644
--- a/contrib/libs/liburing/test/mkdir.t/ya.make
+++ b/contrib/libs/liburing/test/mkdir.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/mock_file.t/ya.make b/contrib/libs/liburing/test/mock_file.t/ya.make
index bfa1d234756..26ea680c7d3 100644
--- a/contrib/libs/liburing/test/mock_file.t/ya.make
+++ b/contrib/libs/liburing/test/mock_file.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/msg-ring-fd.t/ya.make b/contrib/libs/liburing/test/msg-ring-fd.t/ya.make
index a0ba8b2a8ac..ad79af8f8cb 100644
--- a/contrib/libs/liburing/test/msg-ring-fd.t/ya.make
+++ b/contrib/libs/liburing/test/msg-ring-fd.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/msg-ring-flags.t/ya.make b/contrib/libs/liburing/test/msg-ring-flags.t/ya.make
index 64a0f69dc41..190bae48326 100644
--- a/contrib/libs/liburing/test/msg-ring-flags.t/ya.make
+++ b/contrib/libs/liburing/test/msg-ring-flags.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/msg-ring-overflow.t/ya.make b/contrib/libs/liburing/test/msg-ring-overflow.t/ya.make
index 152efd7d51e..f31446bfb5f 100644
--- a/contrib/libs/liburing/test/msg-ring-overflow.t/ya.make
+++ b/contrib/libs/liburing/test/msg-ring-overflow.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/msg-ring.t/ya.make b/contrib/libs/liburing/test/msg-ring.t/ya.make
index 5115e640865..58a50a3a804 100644
--- a/contrib/libs/liburing/test/msg-ring.t/ya.make
+++ b/contrib/libs/liburing/test/msg-ring.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/mshot-shutdown-race.c b/contrib/libs/liburing/test/mshot-shutdown-race.c
new file mode 100644
index 00000000000..22f7288ff7b
--- /dev/null
+++ b/contrib/libs/liburing/test/mshot-shutdown-race.c
@@ -0,0 +1,227 @@
+#include "../config-host.h"
+// SPDX-License-Identifier: MIT
+/*
+ * Description: test AF_UNIX multishot handling of a send immediately
+ * followed by a shutdown of the socket, testing that neither
+ * of those events even if they both arrive before the kernel
+ * can react to the first one. Based on a reproducer from a
+ * bug report here:
+ *
+ * https://github.com/axboe/liburing/issues/1549
+ */
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define DUMMY_SEND_SIZE 123
+#define BUF_COUNT 4
+#define BUF_SIZE 256
+#define BUF_GRP 0
+#define ITERATIONS 10000
+
+struct thread_data {
+ pthread_t thread;
+ pthread_barrier_t barrier;
+ int server_socket;
+ int client_socket;
+ int nr_sends;
+ int stop;
+};
+
+static bool use_af_inet;
+static int received_bytes;
+
+#define CHECK(x) do { \
+ if (!(x)) { \
+ fprintf(stderr, "Error: " #x "\n"); \
+ _exit(1); \
+ } \
+} while (0)
+
+static void *client_thread(void *arg)
+{
+ static char buf[DUMMY_SEND_SIZE];
+ struct thread_data *td = arg;
+ int res;
+
+ while (!td->stop) {
+ int j;
+
+ pthread_barrier_wait(&td->barrier);
+
+ for (j = 0; j < td->nr_sends; j++) {
+ res = send(td->client_socket, buf, sizeof(buf), 0);
+ CHECK(res == sizeof(buf));
+ }
+
+ res = shutdown(td->client_socket, SHUT_WR);
+ CHECK(res == 0);
+ }
+
+ return NULL;
+}
+
+static void create_sockets(struct thread_data *td)
+{
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = 0,
+ .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) },
+ };
+ socklen_t addr_len = sizeof(addr);
+ int listen_fd, res;
+
+ if (!use_af_inet) {
+ int sockets[2];
+
+ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
+ td->client_socket = sockets[0];
+ td->server_socket = sockets[1];
+ return;
+ }
+
+ listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ CHECK(listen_fd > 0);
+
+ res = bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr));
+ CHECK(res == 0);
+
+ res = getsockname(listen_fd, (struct sockaddr *)&addr, &addr_len);
+ CHECK(res == 0);
+
+ res = listen(listen_fd, 1);
+ CHECK(res == 0);
+
+ td->client_socket = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ CHECK(td->client_socket > 0);
+
+ res = connect(td->client_socket, (struct sockaddr *)&addr, sizeof(addr));
+ CHECK(res == 0);
+
+ td->server_socket = accept4(listen_fd, NULL, NULL, SOCK_CLOEXEC);
+ CHECK(td->server_socket > 0);
+ close(listen_fd);
+}
+
+static int last_received_bytes;
+
+static void sig_alrm(int sig)
+{
+ if (received_bytes == last_received_bytes) {
+ fprintf(stderr, "Seems stuck, exit\n");
+ exit(1);
+ }
+ last_received_bytes = received_bytes;
+ alarm(5);
+}
+
+int main(int argc, char *argv[])
+{
+ static char buffer_bytes[BUF_COUNT * BUF_SIZE];
+ struct io_uring_buf_ring *ring;
+ struct sigaction act = { };
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring iouring;
+ struct thread_data td;
+ bool received_eof;
+ uint16_t buffer_id;
+ int err, i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ act.sa_handler = sig_alrm;
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &act, NULL);
+ alarm(5);
+
+ use_af_inet = !!getenv("TEST_USE_INET");
+
+ CHECK(io_uring_queue_init(8, &iouring, 0) >= 0);
+
+ pthread_barrier_init(&td.barrier, NULL, 2);
+ td.stop = 0;
+
+ pthread_create(&td.thread, NULL, client_thread, &td);
+ pthread_detach(td.thread);
+
+ ring = io_uring_setup_buf_ring(&iouring, BUF_COUNT, BUF_GRP, 0, &err);
+ CHECK(err == 0);
+
+ for (i = 0; i < BUF_COUNT; i++) {
+ io_uring_buf_ring_add(ring, &buffer_bytes[BUF_SIZE * i],
+ BUF_SIZE, (uint16_t)i,
+ BUF_COUNT - 1, i);
+ }
+ io_uring_buf_ring_advance(ring, BUF_COUNT);
+
+ for (i = 0; i < ITERATIONS; i++) {
+ int expected_bytes;
+
+ td.nr_sends = (i & 1) + 1;
+ expected_bytes = td.nr_sends * DUMMY_SEND_SIZE;
+
+ create_sockets(&td);
+
+ sqe = io_uring_get_sqe(&iouring);
+ CHECK(sqe);
+ io_uring_prep_recv_multishot(sqe, td.server_socket, NULL, 0, 0);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BUF_GRP;
+
+ io_uring_submit(&iouring);
+
+ pthread_barrier_wait(&td.barrier);
+
+ received_bytes = 0;
+ received_eof = false;
+
+ while (!received_eof) {
+ cqe = NULL;
+ do {
+ io_uring_wait_cqe(&iouring, &cqe);
+ } while (!cqe);
+
+ if (cqe->res > 0) {
+ CHECK(cqe->flags & IORING_CQE_F_MORE);
+ CHECK(cqe->flags & IORING_CQE_F_BUFFER);
+
+ buffer_id = cqe->flags >> 16;
+
+ io_uring_buf_ring_add(ring,
+ &buffer_bytes[BUF_SIZE * buffer_id],
+ BUF_SIZE, buffer_id,
+ BUF_COUNT - 1, buffer_id);
+ io_uring_buf_ring_advance(ring, 1);
+
+ received_bytes += cqe->res;
+ } else if (cqe->res == 0) {
+ CHECK(!received_eof);
+ received_eof = true;
+
+ CHECK(!(cqe->flags & IORING_CQE_F_MORE));
+ CHECK(!(cqe->flags & IORING_CQE_F_BUFFER));
+ } else {
+ CHECK(!"Unexpected recv");
+ }
+
+ io_uring_cqe_seen(&iouring, cqe);
+ }
+
+ CHECK(received_bytes == expected_bytes);
+
+ close(td.client_socket);
+ close(td.server_socket);
+ }
+
+ td.stop = 1;
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/mshot-shutdown-race.t/ya.make b/contrib/libs/liburing/test/mshot-shutdown-race.t/ya.make
new file mode 100644
index 00000000000..f88503991fb
--- /dev/null
+++ b/contrib/libs/liburing/test/mshot-shutdown-race.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ mshot-shutdown-race.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/multicqes_drain.t/ya.make b/contrib/libs/liburing/test/multicqes_drain.t/ya.make
index 7950cdeefb2..c1f1054181a 100644
--- a/contrib/libs/liburing/test/multicqes_drain.t/ya.make
+++ b/contrib/libs/liburing/test/multicqes_drain.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/napi-test.t/ya.make b/contrib/libs/liburing/test/napi-test.t/ya.make
index 6e9b44aaf22..a58c930d1c5 100644
--- a/contrib/libs/liburing/test/napi-test.t/ya.make
+++ b/contrib/libs/liburing/test/napi-test.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/no-mmap-inval.t/ya.make b/contrib/libs/liburing/test/no-mmap-inval.t/ya.make
index 8f2157b97b2..e0b1a7e1227 100644
--- a/contrib/libs/liburing/test/no-mmap-inval.t/ya.make
+++ b/contrib/libs/liburing/test/no-mmap-inval.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/nolibc.t/ya.make b/contrib/libs/liburing/test/nolibc.t/ya.make
index 0bc32f4a231..e6f08f617d9 100644
--- a/contrib/libs/liburing/test/nolibc.t/ya.make
+++ b/contrib/libs/liburing/test/nolibc.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/nop-all-sizes.t/ya.make b/contrib/libs/liburing/test/nop-all-sizes.t/ya.make
index fd01c8a9db4..c3068f5dd31 100644
--- a/contrib/libs/liburing/test/nop-all-sizes.t/ya.make
+++ b/contrib/libs/liburing/test/nop-all-sizes.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/nop-fixed-file-leak.c b/contrib/libs/liburing/test/nop-fixed-file-leak.c
new file mode 100644
index 00000000000..9e02d5a1e86
--- /dev/null
+++ b/contrib/libs/liburing/test/nop-fixed-file-leak.c
@@ -0,0 +1,101 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test for a struct file reference leak in IORING_OP_NOP.
+ *
+ * When an SQE carries IOSQE_FIXED_FILE (which sets REQ_F_FIXED_FILE at
+ * prep time) together with IORING_NOP_FILE but *without*
+ * IORING_NOP_FIXED_FILE, io_nop() takes a normal file reference via
+ * io_file_get_normal(), but io_put_file() won't drop the ref as
+ * REQ_F_FIXED_FILE is set.
+ *
+ * Reported-by: [email protected]
+ * Fixes: a85f31052bce ("io_uring/nop: add support to fixed and normal files")
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#ifndef IORING_NOP_FILE
+#define IORING_NOP_FILE (1U << 1)
+#endif
+#ifndef IORING_NOP_FIXED_FILE
+#define IORING_NOP_FIXED_FILE (1U << 2)
+#endif
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int pipefd[2];
+ char c = 'x';
+ ssize_t n;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ /* don't die on the EPIPE write in the non-buggy case */
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (pipe(pipefd) < 0) {
+ perror("pipe");
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_FILE;
+ sqe->fd = pipefd[0];
+ sqe->flags |= IOSQE_FIXED_FILE;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ /*
+ * Drop our own reference on the read end. If the kernel leaked the
+ * NOP's reference, the read end stays alive and the write below
+ * succeeds. If correct, the read end is gone and we get -EPIPE.
+ */
+ close(pipefd[0]);
+
+ n = write(pipefd[1], &c, 1);
+ if (n == 1) {
+ fprintf(stderr, "BUG: write to pipe with closed read end "
+ "succeeded -- NOP leaked a file reference\n");
+ ret = T_EXIT_FAIL;
+ } else if (n < 0 && errno == EPIPE) {
+ ret = T_EXIT_PASS;
+ } else {
+ fprintf(stderr, "unexpected write result: n=%zd errno=%d\n",
+ n, errno);
+ ret = T_EXIT_FAIL;
+ }
+
+ close(pipefd[1]);
+ io_uring_queue_exit(&ring);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/nop-fixed-file-leak.t/ya.make b/contrib/libs/liburing/test/nop-fixed-file-leak.t/ya.make
new file mode 100644
index 00000000000..6ae484bce3b
--- /dev/null
+++ b/contrib/libs/liburing/test/nop-fixed-file-leak.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ nop-fixed-file-leak.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/nop-flags.c b/contrib/libs/liburing/test/nop-flags.c
new file mode 100644
index 00000000000..a09ea6d5964
--- /dev/null
+++ b/contrib/libs/liburing/test/nop-flags.c
@@ -0,0 +1,745 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test NOP flags (IORING_NOP_FILE, IORING_NOP_FIXED_FILE,
+ * IORING_NOP_FIXED_BUFFER, IORING_NOP_TW, IORING_NOP_CQE32)
+ *
+ * These flags allow NOP to exercise file lookups, buffer lookups, and
+ * task_work completion paths without real I/O, useful for targeted testing.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#ifndef IORING_NOP_INJECT_RESULT
+#define IORING_NOP_INJECT_RESULT (1U << 0)
+#endif
+#ifndef IORING_NOP_FILE
+#define IORING_NOP_FILE (1U << 1)
+#endif
+#ifndef IORING_NOP_FIXED_FILE
+#define IORING_NOP_FIXED_FILE (1U << 2)
+#endif
+#ifndef IORING_NOP_FIXED_BUFFER
+#define IORING_NOP_FIXED_BUFFER (1U << 3)
+#endif
+#ifndef IORING_NOP_TW
+#define IORING_NOP_TW (1U << 4)
+#endif
+#ifndef IORING_NOP_CQE32
+#define IORING_NOP_CQE32 (1U << 5)
+#endif
+
+static int no_nop_flags;
+static int no_nop_file;
+
+/*
+ * Detect NOP flags support using INJECT_RESULT. A kernel that supports
+ * NOP flags will return the injected value; one that doesn't will
+ * return 0 (plain NOP) or -EINVAL.
+ */
+static int test_nop_detect(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_INJECT_RESULT;
+ sqe->len = 42;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit detect: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait detect: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res != 42) {
+ /* Kernel doesn't support NOP flags */
+ no_nop_flags = 1;
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_SKIP;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_PASS;
+}
+
+/*
+ * Detect IORING_NOP_FILE support by using it with a bad fd.
+ * If supported, returns -EBADF. If not, returns 0 (NOP succeeds,
+ * kernel ignores the FILE flag).
+ */
+static int test_nop_file_bad_fd(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ if (no_nop_flags)
+ return T_EXIT_SKIP;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_FILE;
+ sqe->fd = 9999; /* invalid fd */
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res == 0) {
+ /* Kernel doesn't support IORING_NOP_FILE */
+ no_nop_file = 1;
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_SKIP;
+ }
+
+ if (cqe->res != -EBADF && cqe->res != -EINVAL) {
+ fprintf(stderr, "nop bad fd res: %d (expected -EBADF)\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test IORING_NOP_FILE - NOP with a normal fd lookup
+ */
+static int test_nop_file(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, fd;
+
+ if (no_nop_flags || no_nop_file)
+ return T_EXIT_SKIP;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0) {
+ perror("open /dev/null");
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_FILE;
+ sqe->fd = fd;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ close(fd);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ close(fd);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res != 0) {
+ if (cqe->res == -EINVAL) {
+ no_nop_file = 1;
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "nop file res: %d\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ close(fd);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ close(fd);
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test IORING_NOP_FIXED_FILE - NOP with a fixed file lookup
+ */
+static int test_nop_fixed_file(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, fd;
+
+ if (no_nop_flags || no_nop_file)
+ return T_EXIT_SKIP;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0) {
+ perror("open /dev/null");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_files(ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "register files: %d\n", ret);
+ close(fd);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_FILE | IORING_NOP_FIXED_FILE;
+ sqe->fd = 0; /* fixed file index */
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->res != 0) {
+ fprintf(stderr, "nop fixed file res: %d\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ goto err;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+
+ /* Test with invalid fixed file index */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_FILE | IORING_NOP_FIXED_FILE;
+ sqe->fd = 999; /* invalid index */
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit bad index: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait bad index: %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->res != -EBADF) {
+ fprintf(stderr, "nop bad fixed file res: %d (expected -EBADF)\n",
+ cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ goto err;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ io_uring_unregister_files(ring);
+ close(fd);
+ return T_EXIT_PASS;
+err:
+ io_uring_unregister_files(ring);
+ close(fd);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Test IORING_NOP_FIXED_BUFFER - NOP with a registered buffer lookup
+ */
+static int test_nop_fixed_buffer(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ char buf[4096];
+ struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+ int ret;
+
+ if (no_nop_flags)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_register_buffers(ring, &iov, 1);
+ if (ret) {
+ fprintf(stderr, "register buffers: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Valid buffer index */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_FIXED_BUFFER;
+ sqe->buf_index = 0;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->res != 0) {
+ if (cqe->res == -EINVAL) {
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "nop fixed buffer res: %d\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ /* Invalid buffer index */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_FIXED_BUFFER;
+ sqe->buf_index = 999;
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit bad buf: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait bad buf: %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->res == 0) {
+ /* Kernel doesn't support IORING_NOP_FIXED_BUFFER */
+ io_uring_cqe_seen(ring, cqe);
+ io_uring_unregister_buffers(ring);
+ return T_EXIT_SKIP;
+ }
+ if (cqe->res != -EFAULT) {
+ fprintf(stderr, "nop bad buffer res: %d (expected -EFAULT)\n",
+ cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ goto err;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ io_uring_unregister_buffers(ring);
+ return T_EXIT_PASS;
+err:
+ io_uring_unregister_buffers(ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Test IORING_NOP_TW - NOP completing via task_work
+ */
+static int test_nop_tw(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i;
+
+ if (no_nop_flags)
+ return T_EXIT_SKIP;
+
+ /* Submit several NOPs via task_work path */
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_TW;
+ sqe->user_data = i + 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != 8) {
+ fprintf(stderr, "submit tw: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 8; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait tw: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res != 0) {
+ if (cqe->res == -EINVAL) {
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "nop tw res: %d\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test IORING_NOP_TW combined with IORING_NOP_INJECT_RESULT
+ */
+static int test_nop_tw_inject(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ if (no_nop_flags)
+ return T_EXIT_SKIP;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_TW | IORING_NOP_INJECT_RESULT;
+ sqe->len = 42;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res != 42) {
+ if (cqe->res == -EINVAL) {
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "nop tw inject res: %d (expected 42)\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test IORING_NOP_CQE32 - NOP with extra1/extra2 in big CQE
+ */
+static int test_nop_cqe32(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+
+ if (no_nop_flags)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_CQE32);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "ring setup cqe32: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_CQE32;
+ sqe->off = 0xdeadbeef12345678ULL; /* extra1 */
+ sqe->addr = 0xabcdef0011223344ULL; /* extra2 */
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit cqe32: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe32: %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->res != 0) {
+ if (cqe->res == -EINVAL)
+ goto skip;
+ fprintf(stderr, "nop cqe32 res: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+
+ if (cqe->big_cqe[0] != 0xdeadbeef12345678ULL) {
+ fprintf(stderr, "cqe32 extra1: 0x%llx (expected 0xdeadbeef12345678)\n",
+ (unsigned long long) cqe->big_cqe[0]);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+
+ if (cqe->big_cqe[1] != 0xabcdef0011223344ULL) {
+ fprintf(stderr, "cqe32 extra2: 0x%llx (expected 0xabcdef0011223344)\n",
+ (unsigned long long) cqe->big_cqe[1]);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+skip:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+}
+
+/*
+ * Test IORING_NOP_CQE32 requires CQE32 or CQE_MIXED ring
+ */
+static int test_nop_cqe32_no_ring_support(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+
+ if (no_nop_flags)
+ return T_EXIT_SKIP;
+
+ /* Ring without CQE32 */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_CQE32;
+ sqe->off = 1;
+ sqe->addr = 2;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr, "nop cqe32 no support res: %d (expected -EINVAL)\n",
+ cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test combined flags: FILE + FIXED_FILE + FIXED_BUFFER + TW
+ */
+static int test_nop_combined(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ char buf[4096];
+ struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+ int ret, fd;
+
+ if (no_nop_flags || no_nop_file)
+ return T_EXIT_SKIP;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0) {
+ perror("open /dev/null");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_files(ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "register files: %d\n", ret);
+ close(fd);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_buffers(ring, &iov, 1);
+ if (ret) {
+ fprintf(stderr, "register buffers: %d\n", ret);
+ io_uring_unregister_files(ring);
+ close(fd);
+ return T_EXIT_FAIL;
+ }
+
+ /* All flags combined */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->nop_flags = IORING_NOP_FILE | IORING_NOP_FIXED_FILE |
+ IORING_NOP_FIXED_BUFFER | IORING_NOP_TW;
+ sqe->fd = 0;
+ sqe->buf_index = 0;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit combined: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait combined: %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->res != 0) {
+ fprintf(stderr, "nop combined res: %d\n", cqe->res);
+ io_uring_cqe_seen(ring, cqe);
+ goto err;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ io_uring_unregister_buffers(ring);
+ io_uring_unregister_files(ring);
+ close(fd);
+ return T_EXIT_PASS;
+err:
+ io_uring_unregister_buffers(ring);
+ io_uring_unregister_files(ring);
+ close(fd);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SUBMIT_ALL);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_nop_detect(&ring);
+ if (ret == T_EXIT_SKIP) {
+ printf("NOP flags not supported, skipping\n");
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+ } else if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_detect failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ /* Use bad fd test to detect NOP_FILE support */
+ ret = test_nop_file_bad_fd(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_file_bad_fd failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_nop_file(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_file failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_nop_fixed_file(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_fixed_file failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_nop_fixed_buffer(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_fixed_buffer failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_nop_tw(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_tw failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_nop_tw_inject(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_tw_inject failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+
+ ret = test_nop_cqe32();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_cqe32 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_nop_cqe32_no_ring_support();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_cqe32_no_ring_support failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ /* New ring for combined test since we need fresh file/buffer tables */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_nop_combined(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_nop_combined failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/nop-flags.t/ya.make b/contrib/libs/liburing/test/nop-flags.t/ya.make
new file mode 100644
index 00000000000..b298eac7d5c
--- /dev/null
+++ b/contrib/libs/liburing/test/nop-flags.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ nop-flags.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/nop.t/ya.make b/contrib/libs/liburing/test/nop.t/ya.make
index a34ed79e0be..52ee9a290a6 100644
--- a/contrib/libs/liburing/test/nop.t/ya.make
+++ b/contrib/libs/liburing/test/nop.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/nop32-overflow.t/ya.make b/contrib/libs/liburing/test/nop32-overflow.t/ya.make
index e2357b5afe1..8f1cfc8b23d 100644
--- a/contrib/libs/liburing/test/nop32-overflow.t/ya.make
+++ b/contrib/libs/liburing/test/nop32-overflow.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/nop32.t/ya.make b/contrib/libs/liburing/test/nop32.t/ya.make
index 3d6dab59837..7ba0292e45d 100644
--- a/contrib/libs/liburing/test/nop32.t/ya.make
+++ b/contrib/libs/liburing/test/nop32.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/ooo-file-unreg.t/ya.make b/contrib/libs/liburing/test/ooo-file-unreg.t/ya.make
index 04703ac8128..4dcc6a1383d 100644
--- a/contrib/libs/liburing/test/ooo-file-unreg.t/ya.make
+++ b/contrib/libs/liburing/test/ooo-file-unreg.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/open-close.t/ya.make b/contrib/libs/liburing/test/open-close.t/ya.make
index fdec334ee2d..5194ee14c49 100644
--- a/contrib/libs/liburing/test/open-close.t/ya.make
+++ b/contrib/libs/liburing/test/open-close.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/open-direct-link.t/ya.make b/contrib/libs/liburing/test/open-direct-link.t/ya.make
index 12667944c33..2e38d158148 100644
--- a/contrib/libs/liburing/test/open-direct-link.t/ya.make
+++ b/contrib/libs/liburing/test/open-direct-link.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/open-direct-pick.t/ya.make b/contrib/libs/liburing/test/open-direct-pick.t/ya.make
index 9e15e7637ef..bea92d53ffa 100644
--- a/contrib/libs/liburing/test/open-direct-pick.t/ya.make
+++ b/contrib/libs/liburing/test/open-direct-pick.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/openat2.t/ya.make b/contrib/libs/liburing/test/openat2.t/ya.make
index 617e95d3d5d..e66ef596941 100644
--- a/contrib/libs/liburing/test/openat2.t/ya.make
+++ b/contrib/libs/liburing/test/openat2.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/personality.t/ya.make b/contrib/libs/liburing/test/personality.t/ya.make
index 245fd3b9126..722ce7d4553 100644
--- a/contrib/libs/liburing/test/personality.t/ya.make
+++ b/contrib/libs/liburing/test/personality.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/pipe-bug.t/ya.make b/contrib/libs/liburing/test/pipe-bug.t/ya.make
index 8b25288be20..45d7454fc19 100644
--- a/contrib/libs/liburing/test/pipe-bug.t/ya.make
+++ b/contrib/libs/liburing/test/pipe-bug.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/pipe-direct-fixed.c b/contrib/libs/liburing/test/pipe-direct-fixed.c
new file mode 100644
index 00000000000..2783b7d4108
--- /dev/null
+++ b/contrib/libs/liburing/test/pipe-direct-fixed.c
@@ -0,0 +1,243 @@
+#include "../config-host.h"
+// SPDX-License-Identifier: MIT
+/*
+ * Test pipe creation with specific fixed file slots (not auto-alloc).
+ *
+ * Bug: __io_fixed_fd_install() returns 0 for non-alloc success, but
+ * io_pipe_fixed() used this return value as the slot index for both
+ * the fds reported to userspace and error cleanup. This resulted in:
+ * - fds always reporting {0, 0} instead of the actual slot indices
+ * - error cleanup removing slot 0 instead of the correct slots
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int no_pipe;
+
+/*
+ * Create a pipe at specific fixed slots, verify the returned fds match
+ * the requested slots, and verify communication works through them.
+ */
+static int test_specific_slots(int slot)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ char src[32], dst[32];
+ int ret, fds[2];
+ int i;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_register_files_sparse(&ring, 20);
+ if (ret) {
+ if (ret == -EINVAL) {
+ no_pipe = 1;
+ io_uring_queue_exit(&ring);
+ return 0;
+ }
+ fprintf(stderr, "register_files_sparse: %d\n", ret);
+ goto fail;
+ }
+
+ fds[0] = fds[1] = -1;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_pipe_direct(sqe, fds, 0, slot);
+ io_uring_submit(&ring);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto fail;
+ }
+ if (cqe->res) {
+ if (cqe->res == -EINVAL) {
+ no_pipe = 1;
+ io_uring_queue_exit(&ring);
+ return 0;
+ }
+ fprintf(stderr, "pipe cqe res: %d\n", cqe->res);
+ goto fail;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ /* Verify returned fds are the correct 0-based slot indices */
+ if (fds[0] != slot) {
+ fprintf(stderr, "fds[0]=%d, expected %d\n", fds[0], slot);
+ goto fail;
+ }
+ if (fds[1] != slot + 1) {
+ fprintf(stderr, "fds[1]=%d, expected %d\n", fds[1], slot + 1);
+ goto fail;
+ }
+
+ /* Verify pipe works through the fixed file slots */
+ memset(src, 0x5a, sizeof(src));
+ memset(dst, 0, sizeof(dst));
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_write(sqe, fds[1], src, sizeof(src), 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ sqe->user_data = 1;
+ io_uring_submit(&ring);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fds[0], dst, sizeof(dst), 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ sqe->user_data = 2;
+ io_uring_submit(&ring);
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io wait: %d\n", ret);
+ goto fail;
+ }
+ if (cqe->res != sizeof(src)) {
+ fprintf(stderr, "ud=%d res=%d\n",
+ (int)cqe->user_data, cqe->res);
+ goto fail;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ if (memcmp(src, dst, sizeof(src))) {
+ fprintf(stderr, "data mismatch\n");
+ goto fail;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+fail:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+/*
+ * Verify that creating a pipe at specific fixed slots doesn't accidentally
+ * clobber slot 0. Without the fix, fds would be {0, 0} and error cleanup
+ * would remove slot 0 instead of the actual pipe slots.
+ */
+static int test_no_clobber_slot0(void)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, fds[2], pipe_fds[2];
+ int sentinel_fd;
+ char buf;
+
+ if (pipe(pipe_fds)) {
+ fprintf(stderr, "pipe\n");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_register_files_sparse(&ring, 20);
+ if (ret) {
+ fprintf(stderr, "register_files_sparse: %d\n", ret);
+ goto err;
+ }
+
+ /* Put a sentinel pipe-read-end at slot 0 */
+ sentinel_fd = pipe_fds[0];
+ ret = io_uring_register_files_update(&ring, 0, &sentinel_fd, 1);
+ if (ret != 1) {
+ fprintf(stderr, "sentinel install: %d\n", ret);
+ goto err;
+ }
+
+ /* Create pipe at specific slots 5 and 6 */
+ fds[0] = fds[1] = -1;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_pipe_direct(sqe, fds, 0, 5);
+ io_uring_submit(&ring);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ goto err;
+ }
+ if (cqe->res) {
+ fprintf(stderr, "pipe cqe res: %d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ /* Verify slot 0 sentinel is still intact by reading through it */
+ ret = write(pipe_fds[1], "x", 1);
+ if (ret != 1) {
+ fprintf(stderr, "sentinel write: %d\n", ret);
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, 0, &buf, 1, 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ io_uring_submit(&ring);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait sentinel: %d\n", ret);
+ goto err;
+ }
+ if (cqe->res != 1) {
+ fprintf(stderr, "sentinel read res=%d, slot 0 may have been clobbered\n",
+ cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_specific_slots(5);
+ if (ret) {
+ fprintf(stderr, "test_specific_slots(5) failed\n");
+ return T_EXIT_FAIL;
+ }
+ if (no_pipe)
+ return T_EXIT_SKIP;
+
+ ret = test_specific_slots(0);
+ if (ret) {
+ fprintf(stderr, "test_specific_slots(0) failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_no_clobber_slot0();
+ if (ret) {
+ fprintf(stderr, "test_no_clobber_slot0 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/pipe-direct-fixed.t/ya.make b/contrib/libs/liburing/test/pipe-direct-fixed.t/ya.make
new file mode 100644
index 00000000000..e8c27e87578
--- /dev/null
+++ b/contrib/libs/liburing/test/pipe-direct-fixed.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ pipe-direct-fixed.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/pipe-eof.t/ya.make b/contrib/libs/liburing/test/pipe-eof.t/ya.make
index 22d79e5333b..ea04fd8d549 100644
--- a/contrib/libs/liburing/test/pipe-eof.t/ya.make
+++ b/contrib/libs/liburing/test/pipe-eof.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/pipe-reuse.t/ya.make b/contrib/libs/liburing/test/pipe-reuse.t/ya.make
index 80ed0afb155..b1f66a3619e 100644
--- a/contrib/libs/liburing/test/pipe-reuse.t/ya.make
+++ b/contrib/libs/liburing/test/pipe-reuse.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/pipe.t/ya.make b/contrib/libs/liburing/test/pipe.t/ya.make
index 9f2061f4369..32a481f1035 100644
--- a/contrib/libs/liburing/test/pipe.t/ya.make
+++ b/contrib/libs/liburing/test/pipe.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-cancel-all.t/ya.make b/contrib/libs/liburing/test/poll-cancel-all.t/ya.make
index 2719a9904ce..8956318d8dd 100644
--- a/contrib/libs/liburing/test/poll-cancel-all.t/ya.make
+++ b/contrib/libs/liburing/test/poll-cancel-all.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-cancel-ton.t/ya.make b/contrib/libs/liburing/test/poll-cancel-ton.t/ya.make
index 90b24236a50..8b88c31c48a 100644
--- a/contrib/libs/liburing/test/poll-cancel-ton.t/ya.make
+++ b/contrib/libs/liburing/test/poll-cancel-ton.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-cancel.t/ya.make b/contrib/libs/liburing/test/poll-cancel.t/ya.make
index 290d88c7825..f36a6539bff 100644
--- a/contrib/libs/liburing/test/poll-cancel.t/ya.make
+++ b/contrib/libs/liburing/test/poll-cancel.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-link.t/ya.make b/contrib/libs/liburing/test/poll-link.t/ya.make
index acfdb3b96a8..56696161200 100644
--- a/contrib/libs/liburing/test/poll-link.t/ya.make
+++ b/contrib/libs/liburing/test/poll-link.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-many.t/ya.make b/contrib/libs/liburing/test/poll-many.t/ya.make
index d2a50286cfa..1cf601a578b 100644
--- a/contrib/libs/liburing/test/poll-many.t/ya.make
+++ b/contrib/libs/liburing/test/poll-many.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-mshot-overflow.t/ya.make b/contrib/libs/liburing/test/poll-mshot-overflow.t/ya.make
index ddcca20284d..16a87a46544 100644
--- a/contrib/libs/liburing/test/poll-mshot-overflow.t/ya.make
+++ b/contrib/libs/liburing/test/poll-mshot-overflow.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-mshot-update.t/ya.make b/contrib/libs/liburing/test/poll-mshot-update.t/ya.make
index 913faa782a2..f41e691b50e 100644
--- a/contrib/libs/liburing/test/poll-mshot-update.t/ya.make
+++ b/contrib/libs/liburing/test/poll-mshot-update.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-mshot-wake.c b/contrib/libs/liburing/test/poll-mshot-wake.c
new file mode 100644
index 00000000000..8340d562ca7
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-mshot-wake.c
@@ -0,0 +1,127 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test multishot POLL_ADD on an eventfd that is also the
+ * CQ notification eventfd of the same ring. Triggering the poll
+ * posts a CQE, which notifies the eventfd, which re-enters the
+ * poll wake path via EPOLL_URING_WAKE. The multishot poll must
+ * keep delivering events for subsequent wakeups; a regression
+ * here leaves the poll request permanently stuck with
+ * IORING_CQE_F_MORE set but no further CQEs.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define NR_LOOPS 2
+
+static int test(void)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int ret, evfd, i;
+ uint64_t val;
+
+ evfd = eventfd(0, EFD_NONBLOCK);
+ if (evfd < 0) {
+ perror("eventfd");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ close(evfd);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_eventfd(&ring, evfd);
+ if (ret) {
+ fprintf(stderr, "register_eventfd: %d\n", ret);
+ close(evfd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_multishot(sqe, evfd, POLLIN);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < NR_LOOPS; i++) {
+ val = 1;
+ ret = write(evfd, &val, sizeof(val));
+ if (ret != sizeof(val)) {
+ perror("write");
+ goto err;
+ }
+ ret = read(evfd, &val, sizeof(val));
+ if (ret != sizeof(val)) {
+ perror("read");
+ goto err;
+ }
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+ if (ret == -ETIME) {
+ fprintf(stderr, "poll stuck: no CQE after iteration %d\n", i);
+ goto err;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ goto err;
+ }
+ if (cqe->user_data != 1) {
+ fprintf(stderr, "unexpected user_data %llx\n",
+ (unsigned long long) cqe->user_data);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe res: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err;
+ }
+ if (!i) {
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ fprintf(stderr, "IORING_CQE_F_MORE not set\n");
+ goto err;
+ }
+ } else {
+ if (cqe->flags & IORING_CQE_F_MORE) {
+ fprintf(stderr, "IORING_CQE_F_MORE set\n");
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ close(evfd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ close(evfd);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ return test();
+}
diff --git a/contrib/libs/liburing/test/poll-mshot-wake.t/ya.make b/contrib/libs/liburing/test/poll-mshot-wake.t/ya.make
new file mode 100644
index 00000000000..fb72ce50ec4
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-mshot-wake.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ poll-mshot-wake.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/poll-race-mshot.t/ya.make b/contrib/libs/liburing/test/poll-race-mshot.t/ya.make
index b634613087d..d6334ac85d7 100644
--- a/contrib/libs/liburing/test/poll-race-mshot.t/ya.make
+++ b/contrib/libs/liburing/test/poll-race-mshot.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-race.t/ya.make b/contrib/libs/liburing/test/poll-race.t/ya.make
index 93580eb2536..601b76160c5 100644
--- a/contrib/libs/liburing/test/poll-race.t/ya.make
+++ b/contrib/libs/liburing/test/poll-race.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-ring.t/ya.make b/contrib/libs/liburing/test/poll-ring.t/ya.make
index df177944ec4..e94c07ab1fc 100644
--- a/contrib/libs/liburing/test/poll-ring.t/ya.make
+++ b/contrib/libs/liburing/test/poll-ring.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-update-trigger.c b/contrib/libs/liburing/test/poll-update-trigger.c
index 290bf00a4b5..5fd0f6ec1a0 100644
--- a/contrib/libs/liburing/test/poll-update-trigger.c
+++ b/contrib/libs/liburing/test/poll-update-trigger.c
@@ -52,9 +52,7 @@ int main(int argc, char *argv[])
* writeable.
*/
sqe = io_uring_get_sqe(&ring);
- io_uring_prep_poll_remove(sqe, 1);
- sqe->len = IORING_POLL_UPDATE_EVENTS;
- sqe->poll32_events = POLLOUT;
+ io_uring_prep_poll_update(sqe, 1, 0, POLLOUT, IORING_POLL_UPDATE_EVENTS);
sqe->user_data = 2;
io_uring_submit(&ring);
diff --git a/contrib/libs/liburing/test/poll-update-trigger.t/ya.make b/contrib/libs/liburing/test/poll-update-trigger.t/ya.make
index c72d8df9cf5..5c40d8088b4 100644
--- a/contrib/libs/liburing/test/poll-update-trigger.t/ya.make
+++ b/contrib/libs/liburing/test/poll-update-trigger.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll-v-poll.t/ya.make b/contrib/libs/liburing/test/poll-v-poll.t/ya.make
index 60031c2114e..377a4c4c505 100644
--- a/contrib/libs/liburing/test/poll-v-poll.t/ya.make
+++ b/contrib/libs/liburing/test/poll-v-poll.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/poll.t/ya.make b/contrib/libs/liburing/test/poll.t/ya.make
index 3434f02230d..b665122c90f 100644
--- a/contrib/libs/liburing/test/poll.t/ya.make
+++ b/contrib/libs/liburing/test/poll.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/pollfree.t/ya.make b/contrib/libs/liburing/test/pollfree.t/ya.make
index f4fa17018a9..b76b825d2c4 100644
--- a/contrib/libs/liburing/test/pollfree.t/ya.make
+++ b/contrib/libs/liburing/test/pollfree.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/probe.t/ya.make b/contrib/libs/liburing/test/probe.t/ya.make
index 463a00b338d..eb36d1cd8fb 100644
--- a/contrib/libs/liburing/test/probe.t/ya.make
+++ b/contrib/libs/liburing/test/probe.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/read-before-exit.t/ya.make b/contrib/libs/liburing/test/read-before-exit.t/ya.make
index b5f3af7c994..6acb7b8229f 100644
--- a/contrib/libs/liburing/test/read-before-exit.t/ya.make
+++ b/contrib/libs/liburing/test/read-before-exit.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/read-inc-buf-more.c b/contrib/libs/liburing/test/read-inc-buf-more.c
new file mode 100644
index 00000000000..b035f7c602f
--- /dev/null
+++ b/contrib/libs/liburing/test/read-inc-buf-more.c
@@ -0,0 +1,280 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test that IORING_CQE_F_BUF_MORE is correctly set for
+ * incremental provided buffer rings (IOU_PBUF_RING_INC).
+ *
+ * Two bugs existed:
+ * 1) BUF_MORE was never set for non-pollable files (regular files),
+ * because the early buffer commit path discarded the information.
+ * 2) BUF_MORE was not set at EOF (zero-length read), even though
+ * the buffer still had remaining space.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define BUF_BGID 1
+#define BUF_BID 0
+#define BUF_SIZE 256
+#define READ_SIZE 32
+
+static int no_buf_ring_inc;
+
+static int do_read(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, fd, NULL, READ_SIZE, -1);
+ sqe->flags = IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BUF_BGID;
+
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return -1;
+ }
+
+ ret = cqe->res;
+ if (ret < 0) {
+ fprintf(stderr, "read error: %d\n", ret);
+ io_uring_cqe_seen(ring, cqe);
+ return -1;
+ }
+
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "no buffer flag set\n");
+ io_uring_cqe_seen(ring, cqe);
+ return -1;
+ }
+
+ /*
+ * If we got data and the buffer still has space, BUF_MORE must be
+ * set. If we got EOF (res=0), BUF_MORE must also be set because the
+ * buffer was not consumed. In both cases, the 256 byte buffer has
+ * room left after a <= 32 byte read.
+ */
+ if (!(cqe->flags & IORING_CQE_F_BUF_MORE)) {
+ fprintf(stderr, "BUF_MORE not set, res=%d flags=0x%x\n",
+ cqe->res, cqe->flags);
+ io_uring_cqe_seen(ring, cqe);
+ return -1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+/*
+ * Test BUF_MORE with a pipe (pollable fd). Exercises the normal locked
+ * commit path, and tests EOF handling.
+ */
+static int test_pipe(void)
+{
+ struct io_uring_buf_ring *br;
+ struct io_uring ring;
+ int ret, fds[2];
+ char *buf;
+
+ ret = io_uring_queue_init(64, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ if (pipe(fds) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ if (posix_memalign((void **) &buf, 4096, BUF_SIZE))
+ return 1;
+
+ br = io_uring_setup_buf_ring(&ring, 1, BUF_BGID, IOU_PBUF_RING_INC,
+ &ret);
+ if (!br) {
+ if (ret == -EINVAL) {
+ no_buf_ring_inc = 1;
+ free(buf);
+ close(fds[0]);
+ close(fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+ }
+ fprintf(stderr, "buffer ring register failed: %d\n", ret);
+ return 1;
+ }
+
+ io_uring_buf_ring_add(br, buf, BUF_SIZE, BUF_BID, 0, 0);
+ io_uring_buf_ring_advance(br, 1);
+
+ memset(buf, 0, BUF_SIZE);
+
+ /* Write some data and read it - BUF_MORE should be set */
+ ret = write(fds[1], "hello world!", 12);
+ if (ret != 12) {
+ perror("write");
+ return 1;
+ }
+
+ ret = do_read(&ring, fds[0]);
+ if (ret < 0) {
+ fprintf(stderr, "pipe data read failed\n");
+ return 1;
+ }
+ if (ret != 12) {
+ fprintf(stderr, "pipe short read: %d\n", ret);
+ return 1;
+ }
+
+ /* Close write end, read should get EOF - BUF_MORE should be set
+ * because the buffer still has space */
+ close(fds[1]);
+
+ ret = do_read(&ring, fds[0]);
+ if (ret < 0) {
+ fprintf(stderr, "pipe EOF read failed\n");
+ return 1;
+ }
+ if (ret != 0) {
+ fprintf(stderr, "expected EOF, got %d\n", ret);
+ return 1;
+ }
+
+ io_uring_free_buf_ring(&ring, br, 1, BUF_BGID);
+ io_uring_queue_exit(&ring);
+ free(buf);
+ close(fds[0]);
+ return 0;
+}
+
+/*
+ * Test BUF_MORE with a regular file (non-pollable fd). Exercises the
+ * early commit path where io_should_commit() returns true.
+ */
+static int test_file(void)
+{
+ struct io_uring_buf_ring *br;
+ struct io_uring ring;
+ char fname[64];
+ int ret, fd, i;
+ char *buf;
+
+ sprintf(fname, ".read-inc-buf-more.%d", getpid());
+
+ fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+ for (i = 0; i < 4; i++) {
+ char tmp[READ_SIZE];
+
+ memset(tmp, 'a' + i, sizeof(tmp));
+ ret = write(fd, tmp, sizeof(tmp));
+ if (ret != sizeof(tmp)) {
+ perror("write");
+ unlink(fname);
+ return 1;
+ }
+ }
+ close(fd);
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ perror("open read");
+ unlink(fname);
+ return 1;
+ }
+
+ ret = io_uring_queue_init(64, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ unlink(fname);
+ return 1;
+ }
+
+ if (posix_memalign((void **) &buf, 4096, BUF_SIZE)) {
+ unlink(fname);
+ return 1;
+ }
+
+ br = io_uring_setup_buf_ring(&ring, 1, BUF_BGID, IOU_PBUF_RING_INC,
+ &ret);
+ if (!br) {
+ if (ret == -EINVAL) {
+ no_buf_ring_inc = 1;
+ free(buf);
+ close(fd);
+ io_uring_queue_exit(&ring);
+ unlink(fname);
+ return 0;
+ }
+ fprintf(stderr, "buffer ring register failed: %d\n", ret);
+ unlink(fname);
+ return 1;
+ }
+
+ io_uring_buf_ring_add(br, buf, BUF_SIZE, BUF_BID, 0, 0);
+ io_uring_buf_ring_advance(br, 1);
+
+ memset(buf, 0, BUF_SIZE);
+
+ /* Read 4 chunks - each should have BUF_MORE since buffer is 256
+ * bytes and each read is 32 bytes */
+ for (i = 0; i < 4; i++) {
+ ret = do_read(&ring, fd);
+ if (ret < 0) {
+ fprintf(stderr, "file read %d failed\n", i);
+ goto err;
+ }
+ if (ret != READ_SIZE) {
+ fprintf(stderr, "file short read %d: %d\n", i, ret);
+ goto err;
+ }
+ }
+
+ io_uring_free_buf_ring(&ring, br, 1, BUF_BGID);
+ io_uring_queue_exit(&ring);
+ free(buf);
+ close(fd);
+ unlink(fname);
+ return 0;
+err:
+ unlink(fname);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_pipe();
+ if (ret) {
+ fprintf(stderr, "test_pipe failed\n");
+ return T_EXIT_FAIL;
+ }
+ if (no_buf_ring_inc)
+ return T_EXIT_SKIP;
+
+ ret = test_file();
+ if (ret) {
+ fprintf(stderr, "test_file failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/read-inc-buf-more.t/ya.make b/contrib/libs/liburing/test/read-inc-buf-more.t/ya.make
new file mode 100644
index 00000000000..f0fbdc9016e
--- /dev/null
+++ b/contrib/libs/liburing/test/read-inc-buf-more.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ read-inc-buf-more.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/read-inc-file.t/ya.make b/contrib/libs/liburing/test/read-inc-file.t/ya.make
index 78fc6851099..b1f96d81f25 100644
--- a/contrib/libs/liburing/test/read-inc-file.t/ya.make
+++ b/contrib/libs/liburing/test/read-inc-file.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/read-mshot-empty.t/ya.make b/contrib/libs/liburing/test/read-mshot-empty.t/ya.make
index 3dad646ad1f..8f9cacc74cc 100644
--- a/contrib/libs/liburing/test/read-mshot-empty.t/ya.make
+++ b/contrib/libs/liburing/test/read-mshot-empty.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/read-mshot-stdin.t/ya.make b/contrib/libs/liburing/test/read-mshot-stdin.t/ya.make
index 2734727da42..95d7ff64e56 100644
--- a/contrib/libs/liburing/test/read-mshot-stdin.t/ya.make
+++ b/contrib/libs/liburing/test/read-mshot-stdin.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/read-mshot.t/ya.make b/contrib/libs/liburing/test/read-mshot.t/ya.make
index d84d953c79d..ceff2db4933 100644
--- a/contrib/libs/liburing/test/read-mshot.t/ya.make
+++ b/contrib/libs/liburing/test/read-mshot.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/read-write.t/ya.make b/contrib/libs/liburing/test/read-write.t/ya.make
index fb722542695..d47377cb6d8 100644
--- a/contrib/libs/liburing/test/read-write.t/ya.make
+++ b/contrib/libs/liburing/test/read-write.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/recv-bundle-buf-len.c b/contrib/libs/liburing/test/recv-bundle-buf-len.c
new file mode 100644
index 00000000000..e9b6c938552
--- /dev/null
+++ b/contrib/libs/liburing/test/recv-bundle-buf-len.c
@@ -0,0 +1,349 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test that a provided buffer ring descriptor's len is not
+ * persistently corrupted by a non-incremental bundle operation
+ * that truncates the head buffer.
+ *
+ * 1) A bundle recv that fails with -EAGAIN (no data available)
+ * must NOT leave the head buffer's len shrunk to the
+ * requested size. A later operation using the same buffer
+ * group must still see the full buffer length.
+ *
+ * 2) A bundle recv that succeeds while truncating the buffer
+ * (sqe->len < buf->len) must post a completion that matches
+ * the actual transfer length, not the original buffer len,
+ * and must consume the buffer correctly.
+ *
+ * Based on the reproducer from Federico Brasili:
+ * https://lore.kernel.org/io-uring/CAAEr8jbY60noGj1fw_k91UJRBkyiRVoS6=nLhZ7Svwidjn4CAA@mail.gmail.com/
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define BGID 1
+#define NR_BUFS 2
+#define BUF_SIZE 4096
+#define SHORT_LEN 32
+
+static int no_buf_ring, no_bundle;
+
+struct test_ctx {
+ struct io_uring ring;
+ struct io_uring_buf_ring *br;
+ void *buf_mem;
+};
+
+static void *buf_addr(struct test_ctx *t, int bid)
+{
+ return (char *) t->buf_mem + bid * BUF_SIZE;
+}
+
+/*
+ * Set up a ring + a non-incremental provided buffer ring with NR_BUFS
+ * buffers of BUF_SIZE each, fully published to the kernel.
+ */
+static int setup(struct test_ctx *t)
+{
+ struct io_uring_params p = { };
+ int ret, i, mask;
+
+ ret = io_uring_queue_init_params(8, &t->ring, &p);
+ if (ret < 0) {
+ fprintf(stderr, "queue init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (!(p.features & IORING_FEAT_RECVSEND_BUNDLE)) {
+ no_bundle = 1;
+ io_uring_queue_exit(&t->ring);
+ return T_EXIT_SKIP;
+ }
+
+ if (posix_memalign(&t->buf_mem, 4096, NR_BUFS * BUF_SIZE)) {
+ perror("posix_memalign");
+ io_uring_queue_exit(&t->ring);
+ return T_EXIT_FAIL;
+ }
+
+ t->br = io_uring_setup_buf_ring(&t->ring, NR_BUFS, BGID, 0, &ret);
+ if (!t->br) {
+ if (ret == -EINVAL) {
+ no_buf_ring = 1;
+ free(t->buf_mem);
+ io_uring_queue_exit(&t->ring);
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "buf ring setup: %d\n", ret);
+ free(t->buf_mem);
+ io_uring_queue_exit(&t->ring);
+ return T_EXIT_FAIL;
+ }
+
+ mask = io_uring_buf_ring_mask(NR_BUFS);
+ for (i = 0; i < NR_BUFS; i++)
+ io_uring_buf_ring_add(t->br, buf_addr(t, i), BUF_SIZE, i, mask, i);
+ io_uring_buf_ring_advance(t->br, NR_BUFS);
+
+ return T_EXIT_PASS;
+}
+
+static void teardown(struct test_ctx *t)
+{
+ io_uring_free_buf_ring(&t->ring, t->br, NR_BUFS, BGID);
+ io_uring_queue_exit(&t->ring);
+ free(t->buf_mem);
+}
+
+static int submit_wait(struct test_ctx *t, struct io_uring_cqe **cqe)
+{
+ int ret;
+
+ ret = io_uring_submit(&t->ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return -1;
+ }
+ ret = io_uring_wait_cqe(&t->ring, cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Case 1: a failed (-EAGAIN) bundle recv on an empty socket must not shrink
+ * the head buffer's len. We verify both the raw descriptor and that a later
+ * unrelated READ using the same group sees the full buffer.
+ */
+static int test_eagain_no_corrupt(void)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct test_ctx t;
+ int ret, sv[2], pfd[2], bid;
+ char *pipe_buf;
+
+ ret = setup(&t);
+ if (ret != T_EXIT_PASS)
+ return ret;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
+ perror("socketpair");
+ teardown(&t);
+ return T_EXIT_FAIL;
+ }
+
+ /* empty socket, MSG_DONTWAIT -> -EAGAIN, bundle, len=1 */
+ sqe = io_uring_get_sqe(&t.ring);
+ io_uring_prep_recv(sqe, sv[0], NULL, 1, MSG_DONTWAIT);
+ sqe->ioprio |= IORING_RECVSEND_BUNDLE;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+
+ if (submit_wait(&t, &cqe))
+ goto fail;
+
+ if (cqe->res == -EINVAL) {
+ no_bundle = 1;
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto skip;
+ }
+ if (cqe->res != -EAGAIN) {
+ fprintf(stderr, "case1: expected -EAGAIN, got %d\n", cqe->res);
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto fail;
+ }
+ io_uring_cqe_seen(&t.ring, cqe);
+
+ /* the failed recv must not have consumed or shrunk buffer 0 */
+ if (t.br->bufs[0].len != BUF_SIZE) {
+ fprintf(stderr, "case1: head buf len corrupted: %u (want %u)\n",
+ t.br->bufs[0].len, BUF_SIZE);
+ goto fail;
+ }
+
+ /*
+ * User-visible impact: an unrelated READ from a pipe using the same
+ * buffer group must consume the full buffer, not the poisoned len.
+ */
+ if (pipe(pfd) < 0) {
+ perror("pipe");
+ goto fail;
+ }
+ pipe_buf = malloc(BUF_SIZE);
+ memset(pipe_buf, 0x5a, BUF_SIZE);
+ if (write(pfd[1], pipe_buf, BUF_SIZE) != BUF_SIZE) {
+ perror("write pipe");
+ free(pipe_buf);
+ close(pfd[0]);
+ close(pfd[1]);
+ goto fail;
+ }
+ free(pipe_buf);
+
+ sqe = io_uring_get_sqe(&t.ring);
+ io_uring_prep_read(sqe, pfd[0], NULL, BUF_SIZE, 0);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+
+ ret = submit_wait(&t, &cqe);
+ close(pfd[0]);
+ close(pfd[1]);
+ if (ret)
+ goto fail;
+
+ if (cqe->res != BUF_SIZE) {
+ fprintf(stderr, "case1: read consumed poisoned len: res=%d "
+ "(want %d)\n", cqe->res, BUF_SIZE);
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto fail;
+ }
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "case1: read did not select a buffer\n");
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto fail;
+ }
+ bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
+ if (bid != 0) {
+ fprintf(stderr, "case1: read used wrong bid %d\n", bid);
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto fail;
+ }
+ io_uring_cqe_seen(&t.ring, cqe);
+
+ close(sv[0]);
+ close(sv[1]);
+ teardown(&t);
+ return T_EXIT_PASS;
+skip:
+ close(sv[0]);
+ close(sv[1]);
+ teardown(&t);
+ return T_EXIT_SKIP;
+fail:
+ close(sv[0]);
+ close(sv[1]);
+ teardown(&t);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Case 2: a successful bundle recv that truncates the buffer (len=32 on a
+ * 4096 buffer) must post a completion matching the actual transfer length
+ * (32), not the buffer's original len, and must read only the truncated
+ * amount even though more data is queued.
+ */
+static int test_success_trim(void)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct test_ctx t;
+ char snd[64], *got;
+ int ret, sv[2], bid, i;
+
+ ret = setup(&t);
+ if (ret != T_EXIT_PASS)
+ return ret;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
+ perror("socketpair");
+ teardown(&t);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < (int) sizeof(snd); i++)
+ snd[i] = 'A' + (i % 26);
+ if (write(sv[1], snd, sizeof(snd)) != sizeof(snd)) {
+ perror("write sock");
+ goto fail;
+ }
+
+ /* 64 bytes available, ask for 32: trim to 32, expect res == 32 */
+ sqe = io_uring_get_sqe(&t.ring);
+ io_uring_prep_recv(sqe, sv[0], NULL, SHORT_LEN, MSG_DONTWAIT);
+ sqe->ioprio |= IORING_RECVSEND_BUNDLE;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+
+ if (submit_wait(&t, &cqe))
+ goto fail;
+
+ if (cqe->res == -EINVAL) {
+ no_bundle = 1;
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto skip;
+ }
+ if (cqe->res != SHORT_LEN) {
+ fprintf(stderr, "case2: expected res=%d, got %d\n",
+ SHORT_LEN, cqe->res);
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto fail;
+ }
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "case2: recv did not select a buffer\n");
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto fail;
+ }
+ bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
+ if (bid != 0) {
+ fprintf(stderr, "case2: recv used wrong bid %d\n", bid);
+ io_uring_cqe_seen(&t.ring, cqe);
+ goto fail;
+ }
+ io_uring_cqe_seen(&t.ring, cqe);
+
+ /* the 32 bytes landed in buffer 0 and match the head of the stream */
+ got = buf_addr(&t, 0);
+ if (memcmp(got, snd, SHORT_LEN)) {
+ fprintf(stderr, "case2: received data mismatch\n");
+ goto fail;
+ }
+
+ close(sv[0]);
+ close(sv[1]);
+ teardown(&t);
+ return T_EXIT_PASS;
+skip:
+ close(sv[0]);
+ close(sv[1]);
+ teardown(&t);
+ return T_EXIT_SKIP;
+fail:
+ close(sv[0]);
+ close(sv[1]);
+ teardown(&t);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_eagain_no_corrupt();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_eagain_no_corrupt failed\n");
+ return T_EXIT_FAIL;
+ }
+ if (no_buf_ring || no_bundle)
+ return T_EXIT_SKIP;
+
+ ret = test_success_trim();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_success_trim failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/recv-bundle-buf-len.t/ya.make b/contrib/libs/liburing/test/recv-bundle-buf-len.t/ya.make
new file mode 100644
index 00000000000..6e3dc3e991a
--- /dev/null
+++ b/contrib/libs/liburing/test/recv-bundle-buf-len.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ recv-bundle-buf-len.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/recv-bundle-short-ooo.t/ya.make b/contrib/libs/liburing/test/recv-bundle-short-ooo.t/ya.make
index c4ab4004cfc..d87c8d66c52 100644
--- a/contrib/libs/liburing/test/recv-bundle-short-ooo.t/ya.make
+++ b/contrib/libs/liburing/test/recv-bundle-short-ooo.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/recv-inc-ooo.t/ya.make b/contrib/libs/liburing/test/recv-inc-ooo.t/ya.make
index e73b8a54b1f..f00b82381dd 100644
--- a/contrib/libs/liburing/test/recv-inc-ooo.t/ya.make
+++ b/contrib/libs/liburing/test/recv-inc-ooo.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/recv-msgall-stream.t/ya.make b/contrib/libs/liburing/test/recv-msgall-stream.t/ya.make
index 55c05df58d1..bcf682e4bbf 100644
--- a/contrib/libs/liburing/test/recv-msgall-stream.t/ya.make
+++ b/contrib/libs/liburing/test/recv-msgall-stream.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/recv-msgall.t/ya.make b/contrib/libs/liburing/test/recv-msgall.t/ya.make
index c6c1dd57e6e..15f2c1d9354 100644
--- a/contrib/libs/liburing/test/recv-msgall.t/ya.make
+++ b/contrib/libs/liburing/test/recv-msgall.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/recv-mshot-drain.c b/contrib/libs/liburing/test/recv-mshot-drain.c
new file mode 100644
index 00000000000..1e1f18539af
--- /dev/null
+++ b/contrib/libs/liburing/test/recv-mshot-drain.c
@@ -0,0 +1,318 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: stress test multishot recv with buffer ring exhaustion and
+ * refill. Verifies that multishot recv handles the case where
+ * the buffer ring runs dry during active receives, and that
+ * data is correctly received after refilling buffers.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define BGID 1
+#define BUF_SIZE 128
+/* Intentionally small to force exhaustion */
+#define NR_BUFS 4
+#define NR_SENDS 4096
+#define SEND_SIZE 64
+
+struct thread_data {
+ pthread_barrier_t connect_barrier;
+ int port;
+};
+
+static void provide_buffers(struct io_uring_buf_ring *br, void *base,
+ int nr_bufs, int buf_size, int start_bid)
+{
+ int i;
+
+ for (i = 0; i < nr_bufs; i++) {
+ void *addr = base + i * buf_size;
+ io_uring_buf_ring_add(br, addr, buf_size, start_bid + i,
+ io_uring_buf_ring_mask(nr_bufs), i);
+ }
+ io_uring_buf_ring_advance(br, nr_bufs);
+}
+
+static void *send_thread(void *arg)
+{
+ struct thread_data *td = arg;
+ struct sockaddr_in saddr;
+ char buf[SEND_SIZE];
+ int fd, ret, i;
+
+ memset(buf, 0xaa, sizeof(buf));
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return (void *)(intptr_t)1;
+ }
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(td->port);
+ inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr);
+
+ pthread_barrier_wait(&td->connect_barrier);
+
+ ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("connect");
+ close(fd);
+ return (void *)(intptr_t)1;
+ }
+
+ /* Send more data than we have buffers for */
+ for (i = 0; i < NR_SENDS; i++) {
+ /* Fill pattern: iteration number */
+ memset(buf, i & 0xff, sizeof(buf));
+ ret = send(fd, buf, sizeof(buf), 0);
+ if (ret < 0) {
+ if (errno == EPIPE)
+ break;
+ perror("send");
+ close(fd);
+ return (void *)(intptr_t)1;
+ }
+ /* Small delay to spread sends over time */
+ if (i % 8 == 7)
+ usleep(1000);
+ }
+
+ close(fd);
+ return NULL;
+}
+
+static int test_recv_drain_refill(void)
+{
+ struct io_uring ring;
+ struct io_uring_buf_ring *br;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct thread_data td;
+ struct sockaddr_in saddr;
+ pthread_t thread;
+ void *buf_base;
+ int sockfd, connfd, ret, val;
+ int total_recv = 0;
+ socklen_t socklen;
+ int port;
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Allocate buffer ring */
+ br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret);
+ if (!br) {
+ if (ret == -EINVAL || ret == -ENOENT) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "buf ring setup: %d\n", ret);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ buf_base = malloc(NR_BUFS * BUF_SIZE);
+ if (!buf_base) {
+ fprintf(stderr, "malloc\n");
+ goto err;
+ }
+
+ provide_buffers(br, buf_base, NR_BUFS, BUF_SIZE, 0);
+
+ /* Setup listening socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ goto err;
+ }
+
+ val = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = 0;
+
+ ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("bind");
+ close(sockfd);
+ goto err;
+ }
+
+ socklen = sizeof(saddr);
+ getsockname(sockfd, (struct sockaddr *)&saddr, &socklen);
+ port = ntohs(saddr.sin_port);
+
+ ret = listen(sockfd, 1);
+ if (ret < 0) {
+ perror("listen");
+ close(sockfd);
+ goto err;
+ }
+
+ /* Start sender thread */
+ td.port = port;
+ pthread_barrier_init(&td.connect_barrier, NULL, 2);
+ pthread_create(&thread, NULL, send_thread, &td);
+ pthread_barrier_wait(&td.connect_barrier);
+
+ socklen = sizeof(saddr);
+ connfd = accept(sockfd, (struct sockaddr *)&saddr, &socklen);
+ if (connfd < 0) {
+ perror("accept");
+ close(sockfd);
+ goto err;
+ }
+
+ /* Submit multishot recv */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_recv_multishot(sqe, connfd, NULL, 0, 0);
+ sqe->buf_group = BGID;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit recv: %d\n", ret);
+ goto err_conn;
+ }
+
+ /*
+ * Process CQEs. The multishot will eventually run out of buffers.
+ * When it does (no IORING_CQE_F_MORE), we refill and re-arm.
+ */
+ while (1) {
+ struct __kernel_timespec ts = { .tv_sec = 2 };
+
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+ if (ret == -ETIME)
+ break;
+ if (ret) {
+ fprintf(stderr, "wait cqe: %d\n", ret);
+ goto err_conn;
+ }
+
+ if (cqe->res == -ENOBUFS) {
+ /* Buffer ring exhausted - refill and re-arm */
+ io_uring_cqe_seen(&ring, cqe);
+ provide_buffers(br, buf_base, NR_BUFS, BUF_SIZE, 0);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_recv_multishot(sqe, connfd, NULL, 0, 0);
+ sqe->buf_group = BGID;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "rearm submit: %d\n", ret);
+ goto err_conn;
+ }
+ continue;
+ }
+
+ if (cqe->res == 0) {
+ /* EOF */
+ io_uring_cqe_seen(&ring, cqe);
+ break;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "recv error: %d\n", cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto err_conn;
+ }
+
+ total_recv += cqe->res;
+
+ /* Return buffer if we got one */
+ if (cqe->flags & IORING_CQE_F_BUFFER) {
+ int bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
+ void *addr = buf_base + bid * BUF_SIZE;
+ io_uring_buf_ring_add(br, addr, BUF_SIZE, bid,
+ io_uring_buf_ring_mask(NR_BUFS), 0);
+ io_uring_buf_ring_advance(br, 1);
+ }
+
+ /* If no F_MORE, multishot terminated - re-arm */
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ io_uring_cqe_seen(&ring, cqe);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_recv_multishot(sqe, connfd, NULL, 0, 0);
+ sqe->buf_group = BGID;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "rearm submit: %d\n", ret);
+ goto err_conn;
+ }
+ continue;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ pthread_join(thread, NULL);
+
+ if (total_recv != NR_SENDS * SEND_SIZE) {
+ fprintf(stderr, "recv %d bytes, expected %d\n",
+ total_recv, NR_SENDS * SEND_SIZE);
+ goto err_conn;
+ }
+
+ close(connfd);
+ close(sockfd);
+ free(buf_base);
+ io_uring_free_buf_ring(&ring, br, NR_BUFS, BGID);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+err_conn:
+ close(connfd);
+ close(sockfd);
+err:
+ free(buf_base);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_recv_drain_refill();
+ if (ret == T_EXIT_SKIP) {
+ printf("Buffer rings not supported, skipping\n");
+ return T_EXIT_SKIP;
+ }
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_recv_drain_refill failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/recv-mshot-drain.t/ya.make b/contrib/libs/liburing/test/recv-mshot-drain.t/ya.make
new file mode 100644
index 00000000000..228f9cdd985
--- /dev/null
+++ b/contrib/libs/liburing/test/recv-mshot-drain.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ recv-mshot-drain.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/recv-mshot-fair.t/ya.make b/contrib/libs/liburing/test/recv-mshot-fair.t/ya.make
index 6a1246ad042..9cd83be7e79 100644
--- a/contrib/libs/liburing/test/recv-mshot-fair.t/ya.make
+++ b/contrib/libs/liburing/test/recv-mshot-fair.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/recv-multishot.c b/contrib/libs/liburing/test/recv-multishot.c
index 86bb6f7784e..62784f35b7e 100644
--- a/contrib/libs/liburing/test/recv-multishot.c
+++ b/contrib/libs/liburing/test/recv-multishot.c
@@ -475,6 +475,65 @@ cleanup:
return ret;
}
+static int test_recvmsg_validate(void)
+{
+ struct io_uring_recvmsg_out *o;
+ unsigned char buf[64];
+ struct msghdr msgh;
+ unsigned int len;
+
+ memset(buf, 0, sizeof(buf));
+ memset(&msgh, 0, sizeof(msgh));
+ o = (struct io_uring_recvmsg_out *)buf;
+
+ msgh.msg_namelen = 4;
+ msgh.msg_controllen = 8;
+ if (!io_uring_recvmsg_validate(buf, 64, &msgh))
+ return -1;
+
+ /* exact fit */
+ if (!io_uring_recvmsg_validate(buf, 28, &msgh))
+ return -1;
+
+ /* one byte short */
+ if (io_uring_recvmsg_validate(buf, 27, &msgh))
+ return -1;
+
+ /* negative buf_len */
+ if (io_uring_recvmsg_validate(buf, -1, &msgh))
+ return -1;
+
+ /* buf smaller than header alone */
+ msgh.msg_namelen = 0;
+ msgh.msg_controllen = 0;
+ if (io_uring_recvmsg_validate(buf, 15, &msgh))
+ return -1;
+
+ /* controllen overflow must be rejected */
+ msgh.msg_controllen = (socklen_t)(~(socklen_t)0 - sizeof(struct io_uring_recvmsg_out));
+ msgh.msg_namelen = 4;
+ if (io_uring_recvmsg_validate(buf, 64, &msgh))
+ return -1;
+
+ msgh.msg_namelen = 4;
+ msgh.msg_controllen = 8;
+ len = io_uring_recvmsg_payload_length(o, 64, &msgh);
+ if (len != 36)
+ return -1;
+
+ /* negative buf_len */
+ if (io_uring_recvmsg_payload_length(o, -1, &msgh) != 0)
+ return -1;
+
+ /* name+ctrl > buf_len must return 0, not wrap around */
+ msgh.msg_namelen = 40;
+ msgh.msg_controllen = 30;
+ if (io_uring_recvmsg_payload_length(o, 64, &msgh) != 0)
+ return -1;
+
+ return 0;
+}
+
static int test_enobuf(void)
{
struct io_uring ring;
@@ -565,6 +624,12 @@ int main(int argc, char *argv[])
has_defer = t_probe_defer_taskrun();
+ ret = test_recvmsg_validate();
+ if (ret) {
+ fprintf(stderr, "test_recvmsg_validate() failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
for (loop = 0; loop < 16; loop++) {
struct args a = {
.stream = loop & 0x01,
diff --git a/contrib/libs/liburing/test/recv-multishot.t/ya.make b/contrib/libs/liburing/test/recv-multishot.t/ya.make
index 9bf6fcd085e..b2671269e11 100644
--- a/contrib/libs/liburing/test/recv-multishot.t/ya.make
+++ b/contrib/libs/liburing/test/recv-multishot.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/recvmsg-inc-tail.c b/contrib/libs/liburing/test/recvmsg-inc-tail.c
new file mode 100644
index 00000000000..9297702f773
--- /dev/null
+++ b/contrib/libs/liburing/test/recvmsg-inc-tail.c
@@ -0,0 +1,319 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Verify recvmsg multishot with IOU_PBUF_RING_INC does not
+ * return spurious -EFAULT (BADADDR) when the tail of a
+ * partially-consumed buffer is smaller than the msghdr header
+ * the kernel needs to place (sizeof(io_uring_recvmsg_out) +
+ * namelen + controllen).
+ *
+ * The kernel must retire the too-small tail and advance to
+ * the next ring entry rather than refusing the recv.
+ *
+ * Also validates the in-buffer layout of each CQE via the
+ * io_uring_recvmsg_* helpers, so a bug that returns a valid
+ * bid but mis-positions the payload is caught.
+ *
+ * See https://github.com/axboe/liburing/issues/1433
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define BGID 1
+#define NR_BUFS 4
+#define BUF_SIZE 1024
+#define QD 8
+
+/*
+ * hdr reserved per CQE by recvmsg multishot:
+ * sizeof(io_uring_recvmsg_out) + namelen + controllen
+ * = 16 + sizeof(struct sockaddr_in) + 0 = 32
+ *
+ * Two send sizes exercise both buffer-advance paths:
+ *
+ * LARGE_SZ=480: two CQEs of (32+480)=512 consume bid=0 exactly. The
+ * kernel advances to bid=1 via the normal head++ path, no retire
+ * needed.
+ *
+ * SMALL_SZ=305: three CQEs of (32+305)=337 in a 1024-byte buffer total
+ * 1011, leaving a 13-byte tail. 13 < 32, so the 4th send into that
+ * buffer must retire the tail and advance to the next ring entry.
+ * Pre-fix this manifests as -EFAULT; post-fix the CQE succeeds with
+ * a new buffer id.
+ *
+ * Send sequence: 2 LARGE + 7 SMALL. Expected bid per CQE:
+ * { 0, 0, 1, 1, 1, 2, 2, 2, 3 }
+ * — one natural transition (0->1) and two retire transitions (1->2
+ * and 2->3).
+ */
+#define LARGE_SZ 480
+#define NR_LARGE 2
+#define SMALL_SZ 305
+#define NR_SMALL 7
+#define NR_SENDS (NR_LARGE + NR_SMALL)
+
+static const int expected_bids[NR_SENDS] = { 0, 0, 1, 1, 1, 2, 2, 2, 3 };
+
+static int no_buf_ring, no_recv_mshot;
+
+static int setup_buf_ring(struct io_uring *ring, void **buf_mem,
+ struct io_uring_buf_ring **out_br)
+{
+ struct io_uring_buf_ring *br;
+ struct io_uring_buf_reg reg = { };
+ size_t total = NR_BUFS * BUF_SIZE;
+ int page_size = sysconf(_SC_PAGESIZE);
+ size_t ring_size;
+ void *mem;
+ int ret, i;
+
+ ring_size = NR_BUFS * sizeof(struct io_uring_buf);
+ ring_size = (ring_size + page_size - 1) & ~(page_size - 1);
+
+ mem = mmap(NULL, total, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (mem == MAP_FAILED)
+ return -1;
+ *buf_mem = mem;
+
+ br = mmap(NULL, ring_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (br == MAP_FAILED) {
+ munmap(mem, total);
+ return -1;
+ }
+ *out_br = br;
+
+ io_uring_buf_ring_init(br);
+ reg.ring_addr = (unsigned long) br;
+ reg.ring_entries = NR_BUFS;
+ reg.bgid = BGID;
+ reg.min_left = 32;
+
+ ret = io_uring_register_buf_ring(ring, &reg, IOU_PBUF_RING_INC);
+ if (ret) {
+ if (ret == -EINVAL) {
+ no_buf_ring = 1;
+ return 0;
+ }
+ fprintf(stderr, "register_buf_ring: %d\n", ret);
+ return -1;
+ }
+
+ for (i = 0; i < NR_BUFS; i++) {
+ io_uring_buf_ring_add(br, (char *)mem + i * BUF_SIZE, BUF_SIZE,
+ i, io_uring_buf_ring_mask(NR_BUFS), i);
+ }
+ io_uring_buf_ring_advance(br, NR_BUFS);
+ return 0;
+}
+
+static int test(void)
+{
+ struct io_uring ring;
+ struct io_uring_buf_ring *br = NULL;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct msghdr msg;
+ struct sockaddr_in name;
+ size_t expected_payload = NR_LARGE * LARGE_SZ + NR_SMALL * SMALL_SZ;
+ uint8_t stream[NR_LARGE * LARGE_SZ + NR_SMALL * SMALL_SZ];
+ size_t bid_offset[NR_BUFS] = { 0 };
+ size_t stream_cursor = 0;
+ size_t sent_offset = 0;
+ void *buf_mem = NULL;
+ int ret, fds[2];
+ int i, seen_bids = 0;
+ int last_bid = -1;
+ int ret_val = T_EXIT_FAIL;
+
+ for (i = 0; i < (int) expected_payload; i++)
+ stream[i] = (uint8_t)(i & 0xff);
+
+ ret = io_uring_queue_init(QD, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (setup_buf_ring(&ring, &buf_mem, &br))
+ goto out;
+ if (no_buf_ring) {
+ ret_val = T_EXIT_SKIP;
+ goto out;
+ }
+
+ ret = t_create_socket_pair(fds, true);
+ if (ret) {
+ fprintf(stderr, "socket_pair: %d\n", ret);
+ goto out;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &name;
+ msg.msg_namelen = sizeof(name);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_recvmsg_multishot(sqe, fds[0], &msg, 0);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = BGID;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto out_close;
+ }
+
+ for (i = 0; i < NR_SENDS; i++) {
+ int hdr = sizeof(struct io_uring_recvmsg_out) + sizeof(name);
+ int send_sz = (i < NR_LARGE) ? LARGE_SZ : SMALL_SZ;
+ struct io_uring_recvmsg_out *o;
+ uint8_t *cqe_buf, *pdata;
+ unsigned int plen;
+ int bid;
+
+ if (write(fds[1], stream + sent_offset, send_sz) != send_sz) {
+ perror("write");
+ goto out_close;
+ }
+ sent_offset += send_sz;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ goto out_close;
+ }
+
+ if (cqe->res == -EINVAL || cqe->res == -ENOTSUP) {
+ no_recv_mshot = 1;
+ io_uring_cqe_seen(&ring, cqe);
+ ret_val = T_EXIT_SKIP;
+ goto out_close;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr,
+ "send %d: recvmsg multishot failed: %s (res=%d)\n",
+ i, strerror(-cqe->res), cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto out_close;
+ }
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "send %d: CQE missing buffer id\n", i);
+ io_uring_cqe_seen(&ring, cqe);
+ goto out_close;
+ }
+ if (cqe->res < hdr) {
+ fprintf(stderr, "send %d: short CQE res=%d (< hdr %d)\n",
+ i, cqe->res, hdr);
+ io_uring_cqe_seen(&ring, cqe);
+ goto out_close;
+ }
+
+ bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
+ if (bid != expected_bids[i]) {
+ fprintf(stderr,
+ "send %d: bid=%d, expected %d\n",
+ i, bid, expected_bids[i]);
+ io_uring_cqe_seen(&ring, cqe);
+ goto out_close;
+ }
+ if (bid != last_bid) {
+ seen_bids++;
+ last_bid = bid;
+ }
+
+ cqe_buf = (uint8_t *)buf_mem + bid * BUF_SIZE + bid_offset[bid];
+ o = io_uring_recvmsg_validate(cqe_buf, cqe->res, &msg);
+ if (!o) {
+ fprintf(stderr,
+ "send %d: recvmsg_validate returned NULL (res=%d)\n",
+ i, cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto out_close;
+ }
+ if (o->controllen != 0) {
+ fprintf(stderr,
+ "send %d: unexpected controllen=%u\n",
+ i, o->controllen);
+ io_uring_cqe_seen(&ring, cqe);
+ goto out_close;
+ }
+
+ plen = io_uring_recvmsg_payload_length(o, cqe->res, &msg);
+ pdata = io_uring_recvmsg_payload(o, &msg);
+
+ if (plen != (unsigned int)(cqe->res - hdr)) {
+ fprintf(stderr,
+ "send %d: payload_length=%u, expected %d\n",
+ i, plen, cqe->res - hdr);
+ io_uring_cqe_seen(&ring, cqe);
+ goto out_close;
+ }
+ if (memcmp(pdata, stream + stream_cursor, plen) != 0) {
+ fprintf(stderr,
+ "send %d: payload content mismatch at cursor %zu, plen %u\n",
+ i, stream_cursor, plen);
+ io_uring_cqe_seen(&ring, cqe);
+ goto out_close;
+ }
+
+ stream_cursor += plen;
+ bid_offset[bid] += cqe->res;
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ if (stream_cursor != expected_payload) {
+ fprintf(stderr, "payload mismatch: got %zu, expected %zu\n",
+ stream_cursor, expected_payload);
+ goto out_close;
+ }
+
+ /*
+ * Four bids expected: 0 (natural advance), 1 (first retire
+ * target), 2 (second retire target), 3 (third retire target).
+ * Anything else means the retire path didn't behave as planned.
+ */
+ if (seen_bids != 4) {
+ fprintf(stderr, "expected 4 distinct bids, saw %d\n",
+ seen_bids);
+ goto out_close;
+ }
+
+ ret_val = T_EXIT_PASS;
+
+out_close:
+ close(fds[0]);
+ close(fds[1]);
+out:
+ io_uring_queue_exit(&ring);
+ if (buf_mem)
+ munmap(buf_mem, NR_BUFS * BUF_SIZE);
+ if (br) {
+ size_t ring_size = NR_BUFS * sizeof(struct io_uring_buf);
+ int page_size = sysconf(_SC_PAGESIZE);
+
+ ring_size = (ring_size + page_size - 1) & ~(page_size - 1);
+ munmap(br, ring_size);
+ }
+ return ret_val;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ return test();
+}
diff --git a/contrib/libs/liburing/test/recvmsg-inc-tail.t/ya.make b/contrib/libs/liburing/test/recvmsg-inc-tail.t/ya.make
new file mode 100644
index 00000000000..ac0383b713d
--- /dev/null
+++ b/contrib/libs/liburing/test/recvmsg-inc-tail.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ recvmsg-inc-tail.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/recvsend_bundle-inc.t/ya.make b/contrib/libs/liburing/test/recvsend_bundle-inc.t/ya.make
index 862ea2ec27d..5e555cbb9fb 100644
--- a/contrib/libs/liburing/test/recvsend_bundle-inc.t/ya.make
+++ b/contrib/libs/liburing/test/recvsend_bundle-inc.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/recvsend_bundle.t/ya.make b/contrib/libs/liburing/test/recvsend_bundle.t/ya.make
index 2364ee2ed0c..983c508e2ec 100644
--- a/contrib/libs/liburing/test/recvsend_bundle.t/ya.make
+++ b/contrib/libs/liburing/test/recvsend_bundle.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/reg-fd-only.t/ya.make b/contrib/libs/liburing/test/reg-fd-only.t/ya.make
index 2930733beb1..d932cad6078 100644
--- a/contrib/libs/liburing/test/reg-fd-only.t/ya.make
+++ b/contrib/libs/liburing/test/reg-fd-only.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/reg-hint.t/ya.make b/contrib/libs/liburing/test/reg-hint.t/ya.make
index b03e911eaee..328cf0d4741 100644
--- a/contrib/libs/liburing/test/reg-hint.t/ya.make
+++ b/contrib/libs/liburing/test/reg-hint.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/reg-reg-ring.t/ya.make b/contrib/libs/liburing/test/reg-reg-ring.t/ya.make
index 1de7d3b599f..0a2f9408010 100644
--- a/contrib/libs/liburing/test/reg-reg-ring.t/ya.make
+++ b/contrib/libs/liburing/test/reg-reg-ring.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/reg-wait.t/ya.make b/contrib/libs/liburing/test/reg-wait.t/ya.make
index 27726cfbc24..fd77b6a5dff 100644
--- a/contrib/libs/liburing/test/reg-wait.t/ya.make
+++ b/contrib/libs/liburing/test/reg-wait.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/regbuf-clone.t/ya.make b/contrib/libs/liburing/test/regbuf-clone.t/ya.make
index d35c403cef1..4635d8715d8 100644
--- a/contrib/libs/liburing/test/regbuf-clone.t/ya.make
+++ b/contrib/libs/liburing/test/regbuf-clone.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/regbuf-merge.t/ya.make b/contrib/libs/liburing/test/regbuf-merge.t/ya.make
index 70e4b0c120b..8f8bbb73362 100644
--- a/contrib/libs/liburing/test/regbuf-merge.t/ya.make
+++ b/contrib/libs/liburing/test/regbuf-merge.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/register-restrictions.t/ya.make b/contrib/libs/liburing/test/register-restrictions.t/ya.make
index ec3e04c9eea..8835d345bd7 100644
--- a/contrib/libs/liburing/test/register-restrictions.t/ya.make
+++ b/contrib/libs/liburing/test/register-restrictions.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/rename.t/ya.make b/contrib/libs/liburing/test/rename.t/ya.make
index de56e6f65f9..5a7522935c5 100644
--- a/contrib/libs/liburing/test/rename.t/ya.make
+++ b/contrib/libs/liburing/test/rename.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/resize-mmap-fail.c b/contrib/libs/liburing/test/resize-mmap-fail.c
new file mode 100644
index 00000000000..3095e026cf6
--- /dev/null
+++ b/contrib/libs/liburing/test/resize-mmap-fail.c
@@ -0,0 +1,125 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test ring resize failure handling when mmap fails.
+ * Ensures the previous ring state remains valid and usable on failure.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/resource.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static long get_vm_size_pages(void)
+{
+ FILE *f = fopen("/proc/self/statm", "r");
+ if (!f)
+ return -1;
+ long pages = 0;
+ if (fscanf(f, "%ld", &pages) != 1)
+ pages = -1;
+ fclose(f);
+ return pages;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = { };
+ struct io_uring ring;
+ struct rlimit old_rlim, new_rlim;
+ long pages, page_size;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ /* Initialize small ring */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Verify original ring works before any resize */
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "Failed to get sqe\n");
+ return T_EXIT_FAIL;
+ }
+ io_uring_prep_nop(sqe);
+ ret = io_uring_submit_and_wait(&ring, 1);
+ if (ret < 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ struct io_uring_cqe *cqe;
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "peek cqe failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ /* Get virtual memory size and pagesize */
+ pages = get_vm_size_pages();
+ page_size = sysconf(_SC_PAGESIZE);
+ if (pages < 0 || page_size < 0) {
+ fprintf(stderr, "Failed to get VM size or page size\n");
+ return T_EXIT_SKIP;
+ }
+
+ /* Get old resource limit */
+ if (getrlimit(RLIMIT_AS, &old_rlim) < 0) {
+ perror("getrlimit");
+ return T_EXIT_FAIL;
+ }
+
+ /* Set tight virtual memory limit to current usage + 2 pages */
+ new_rlim.rlim_cur = (pages + 2) * page_size;
+ new_rlim.rlim_max = old_rlim.rlim_max;
+ if (setrlimit(RLIMIT_AS, &new_rlim) < 0) {
+ perror("setrlimit");
+ return T_EXIT_FAIL;
+ }
+
+ /* Attempt resize to a larger size which is guaranteed to exceed the memory limit */
+ memset(&p, 0, sizeof(p));
+ p.sq_entries = 64;
+ p.cq_entries = 128;
+ ret = io_uring_resize_rings(&ring, &p);
+
+ /* Restore old limit immediately so standard calls are safe */
+ setrlimit(RLIMIT_AS, &old_rlim);
+
+ /* We expect resize to fail with -ENOMEM due to our tight memory limit */
+ if (ret >= 0) {
+ fprintf(stderr, "Resize unexpectedly succeeded under memory pressure\n");
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ /* Ensure the old ring is STILL fully functional and did not crash */
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "Failed to get sqe after failed resize (ring is broken)\n");
+ return T_EXIT_FAIL;
+ }
+ io_uring_prep_nop(sqe);
+ ret = io_uring_submit_and_wait(&ring, 1);
+ if (ret < 0) {
+ fprintf(stderr, "Submit failed after failed resize (ring is broken): %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "Peek cqe failed after failed resize: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/resize-mmap-fail.t/ya.make b/contrib/libs/liburing/test/resize-mmap-fail.t/ya.make
new file mode 100644
index 00000000000..e8503aa3d2e
--- /dev/null
+++ b/contrib/libs/liburing/test/resize-mmap-fail.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ resize-mmap-fail.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/resize-rings.t/ya.make b/contrib/libs/liburing/test/resize-rings.t/ya.make
index 0d77067695e..546d2dc2f6a 100644
--- a/contrib/libs/liburing/test/resize-rings.t/ya.make
+++ b/contrib/libs/liburing/test/resize-rings.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/ring-leak.t/ya.make b/contrib/libs/liburing/test/ring-leak.t/ya.make
index 1a0a2eafddc..1494a0f1ad3 100644
--- a/contrib/libs/liburing/test/ring-leak.t/ya.make
+++ b/contrib/libs/liburing/test/ring-leak.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/ring-leak2.t/ya.make b/contrib/libs/liburing/test/ring-leak2.t/ya.make
index 8ccc6343584..f06ffa73677 100644
--- a/contrib/libs/liburing/test/ring-leak2.t/ya.make
+++ b/contrib/libs/liburing/test/ring-leak2.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/ring-query.c b/contrib/libs/liburing/test/ring-query.c
index 1ad0383a98a..5b9bb868d4e 100644
--- a/contrib/libs/liburing/test/ring-query.c
+++ b/contrib/libs/liburing/test/ring-query.c
@@ -37,7 +37,7 @@ static int io_uring_query(struct io_uring *ring, struct io_uring_query_hdr *arg)
static int test_basic_query(void)
{
- struct io_uring_query_opcode op;
+ struct io_uring_query_opcode op = { };
struct io_uring_query_hdr hdr = {
.query_op = IO_URING_QUERY_OPCODES,
.query_data = uring_ptr_to_u64(&op),
@@ -77,7 +77,7 @@ static int test_basic_query(void)
static int test_invalid(void)
{
int ret;
- struct io_uring_query_opcode op;
+ struct io_uring_query_opcode op = { };
struct io_uring_query_hdr invalid_hdr = {
.query_op = -1U,
.query_data = uring_ptr_to_u64(&op),
@@ -120,7 +120,7 @@ static int test_invalid(void)
static int test_chain(void)
{
int ret;
- struct io_uring_query_opcode op1, op2, op3;
+ struct io_uring_query_opcode op1 = { }, op2 = { }, op3 = { };
struct io_uring_query_hdr hdr3 = {
.query_op = IO_URING_QUERY_OPCODES,
.query_data = uring_ptr_to_u64(&op3),
@@ -164,7 +164,7 @@ static int test_chain(void)
static int test_chain_loop(void)
{
int ret;
- struct io_uring_query_opcode op1, op2;
+ struct io_uring_query_opcode op1 = { }, op2 = { };
struct io_uring_query_hdr hdr2 = {
.query_op = IO_URING_QUERY_OPCODES,
.query_data = uring_ptr_to_u64(&op2),
@@ -179,9 +179,10 @@ static int test_chain_loop(void)
.query_op = IO_URING_QUERY_OPCODES,
.query_data = uring_ptr_to_u64(&op1),
.size = sizeof(struct io_uring_query_opcode),
- .next_entry = uring_ptr_to_u64(&hdr_self_circular),
};
+ hdr_self_circular.next_entry = uring_ptr_to_u64(&hdr_self_circular);
+
hdr1.next_entry = uring_ptr_to_u64(&hdr2);
hdr2.next_entry = uring_ptr_to_u64(&hdr1);
ret = io_uring_query(NULL, &hdr1);
@@ -202,7 +203,7 @@ static int test_chain_loop(void)
static int test_compatibile_shorter(void)
{
int ret;
- struct io_uring_query_opcode_short op;
+ struct io_uring_query_opcode_short op = { };
struct io_uring_query_hdr hdr = {
.query_op = IO_URING_QUERY_OPCODES,
.query_data = uring_ptr_to_u64(&op),
@@ -235,7 +236,7 @@ static int test_compatibile_shorter(void)
static int test_compatibile_larger(void)
{
int ret;
- struct io_uring_query_opcode_large op;
+ struct io_uring_query_opcode_large op = { };
struct io_uring_query_hdr hdr = {
.query_op = IO_URING_QUERY_OPCODES,
.query_data = uring_ptr_to_u64(&op),
diff --git a/contrib/libs/liburing/test/ring-query.t/ya.make b/contrib/libs/liburing/test/ring-query.t/ya.make
index d15b883232f..058ae483969 100644
--- a/contrib/libs/liburing/test/ring-query.t/ya.make
+++ b/contrib/libs/liburing/test/ring-query.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/ringbuf-loop.t/ya.make b/contrib/libs/liburing/test/ringbuf-loop.t/ya.make
index 610935f7fb5..348b22394a2 100644
--- a/contrib/libs/liburing/test/ringbuf-loop.t/ya.make
+++ b/contrib/libs/liburing/test/ringbuf-loop.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/ringbuf-read.c b/contrib/libs/liburing/test/ringbuf-read.c
index 0008eecb58d..f6f00a6dee2 100644
--- a/contrib/libs/liburing/test/ringbuf-read.c
+++ b/contrib/libs/liburing/test/ringbuf-read.c
@@ -138,7 +138,7 @@ int main(int argc, char *argv[])
int ret, fd, i, do_unlink;
if (argc > 1) {
- strcpy(fname, argv[1]);
+ snprintf(fname, sizeof(fname), "%s", argv[1]);
do_unlink = 0;
} else {
sprintf(fname, ".ringbuf-read.%d", getpid());
diff --git a/contrib/libs/liburing/test/ringbuf-read.t/ya.make b/contrib/libs/liburing/test/ringbuf-read.t/ya.make
index 5bbbe56dff2..d36a99b472a 100644
--- a/contrib/libs/liburing/test/ringbuf-read.t/ya.make
+++ b/contrib/libs/liburing/test/ringbuf-read.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/ringbuf-status.t/ya.make b/contrib/libs/liburing/test/ringbuf-status.t/ya.make
index 3c6e1ded3a4..fab611f8350 100644
--- a/contrib/libs/liburing/test/ringbuf-status.t/ya.make
+++ b/contrib/libs/liburing/test/ringbuf-status.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/rsrc_tags.t/ya.make b/contrib/libs/liburing/test/rsrc_tags.t/ya.make
index 3bcc1fadcbf..0b3ea651102 100644
--- a/contrib/libs/liburing/test/rsrc_tags.t/ya.make
+++ b/contrib/libs/liburing/test/rsrc_tags.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/rw_merge_test.t/ya.make b/contrib/libs/liburing/test/rw_merge_test.t/ya.make
index c45dbbc8d03..4c10a9dd451 100644
--- a/contrib/libs/liburing/test/rw_merge_test.t/ya.make
+++ b/contrib/libs/liburing/test/rw_merge_test.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/self.t/ya.make b/contrib/libs/liburing/test/self.t/ya.make
index 2b378c692d1..81bc6657d36 100644
--- a/contrib/libs/liburing/test/self.t/ya.make
+++ b/contrib/libs/liburing/test/self.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/send-zerocopy.t/ya.make b/contrib/libs/liburing/test/send-zerocopy.t/ya.make
index 8f5033945c0..f22c4c64aeb 100644
--- a/contrib/libs/liburing/test/send-zerocopy.t/ya.make
+++ b/contrib/libs/liburing/test/send-zerocopy.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/send_recv.t/ya.make b/contrib/libs/liburing/test/send_recv.t/ya.make
index 757743da05f..56b9677fa14 100644
--- a/contrib/libs/liburing/test/send_recv.t/ya.make
+++ b/contrib/libs/liburing/test/send_recv.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/send_recvmsg.t/ya.make b/contrib/libs/liburing/test/send_recvmsg.t/ya.make
index be56a48df15..06eba7726f6 100644
--- a/contrib/libs/liburing/test/send_recvmsg.t/ya.make
+++ b/contrib/libs/liburing/test/send_recvmsg.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sendmsg_iov_clean.t/ya.make b/contrib/libs/liburing/test/sendmsg_iov_clean.t/ya.make
index 4f2ca110961..a4d8a09525a 100644
--- a/contrib/libs/liburing/test/sendmsg_iov_clean.t/ya.make
+++ b/contrib/libs/liburing/test/sendmsg_iov_clean.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sendzc-bug.t/ya.make b/contrib/libs/liburing/test/sendzc-bug.t/ya.make
index 9a406fb81d8..533e73216af 100644
--- a/contrib/libs/liburing/test/sendzc-bug.t/ya.make
+++ b/contrib/libs/liburing/test/sendzc-bug.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/shared-wq.t/ya.make b/contrib/libs/liburing/test/shared-wq.t/ya.make
index 54b9ee3c40c..cc3c930a7c6 100644
--- a/contrib/libs/liburing/test/shared-wq.t/ya.make
+++ b/contrib/libs/liburing/test/shared-wq.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/short-read.t/ya.make b/contrib/libs/liburing/test/short-read.t/ya.make
index 84b7ea8b80d..d5b06c21b12 100644
--- a/contrib/libs/liburing/test/short-read.t/ya.make
+++ b/contrib/libs/liburing/test/short-read.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/shutdown.t/ya.make b/contrib/libs/liburing/test/shutdown.t/ya.make
index 935205fcc2b..b32a416ea49 100644
--- a/contrib/libs/liburing/test/shutdown.t/ya.make
+++ b/contrib/libs/liburing/test/shutdown.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sigfd-deadlock.t/ya.make b/contrib/libs/liburing/test/sigfd-deadlock.t/ya.make
index 88f2b98b7bd..e6d846f7ee0 100644
--- a/contrib/libs/liburing/test/sigfd-deadlock.t/ya.make
+++ b/contrib/libs/liburing/test/sigfd-deadlock.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/single-issuer.t/ya.make b/contrib/libs/liburing/test/single-issuer.t/ya.make
index 376b616fe99..3fc2cbfc9e3 100644
--- a/contrib/libs/liburing/test/single-issuer.t/ya.make
+++ b/contrib/libs/liburing/test/single-issuer.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/skip-cqe.t/ya.make b/contrib/libs/liburing/test/skip-cqe.t/ya.make
index e018ba16b88..b7ab8dad8f5 100644
--- a/contrib/libs/liburing/test/skip-cqe.t/ya.make
+++ b/contrib/libs/liburing/test/skip-cqe.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/socket-getsetsock-cmd.t/ya.make b/contrib/libs/liburing/test/socket-getsetsock-cmd.t/ya.make
index 2b3bfcbc9bb..68c6329c70b 100644
--- a/contrib/libs/liburing/test/socket-getsetsock-cmd.t/ya.make
+++ b/contrib/libs/liburing/test/socket-getsetsock-cmd.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/socket-io-cmd.t/ya.make b/contrib/libs/liburing/test/socket-io-cmd.t/ya.make
index 501c214d886..bfe43ae38c3 100644
--- a/contrib/libs/liburing/test/socket-io-cmd.t/ya.make
+++ b/contrib/libs/liburing/test/socket-io-cmd.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/socket-nb.t/ya.make b/contrib/libs/liburing/test/socket-nb.t/ya.make
index 30746a81e4c..fdd5c705547 100644
--- a/contrib/libs/liburing/test/socket-nb.t/ya.make
+++ b/contrib/libs/liburing/test/socket-nb.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/socket-rw-eagain.t/ya.make b/contrib/libs/liburing/test/socket-rw-eagain.t/ya.make
index 58c90ed2171..fe7caa311ac 100644
--- a/contrib/libs/liburing/test/socket-rw-eagain.t/ya.make
+++ b/contrib/libs/liburing/test/socket-rw-eagain.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/socket-rw-offset.t/ya.make b/contrib/libs/liburing/test/socket-rw-offset.t/ya.make
index f1b56cdfd8b..ee97c904945 100644
--- a/contrib/libs/liburing/test/socket-rw-offset.t/ya.make
+++ b/contrib/libs/liburing/test/socket-rw-offset.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/socket-rw.t/ya.make b/contrib/libs/liburing/test/socket-rw.t/ya.make
index 9d08c108042..b77c6f1cb1b 100644
--- a/contrib/libs/liburing/test/socket-rw.t/ya.make
+++ b/contrib/libs/liburing/test/socket-rw.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/socket.t/ya.make b/contrib/libs/liburing/test/socket.t/ya.make
index 772dcae3af5..6e1b528c5a9 100644
--- a/contrib/libs/liburing/test/socket.t/ya.make
+++ b/contrib/libs/liburing/test/socket.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/splice.t/ya.make b/contrib/libs/liburing/test/splice.t/ya.make
index e3f3e3a57a9..bfc77e953d9 100644
--- a/contrib/libs/liburing/test/splice.t/ya.make
+++ b/contrib/libs/liburing/test/splice.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sq-full-cpp.t/ya.make b/contrib/libs/liburing/test/sq-full-cpp.t/ya.make
index ba8832e16de..b37c093edc6 100644
--- a/contrib/libs/liburing/test/sq-full-cpp.t/ya.make
+++ b/contrib/libs/liburing/test/sq-full-cpp.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sq-full.t/ya.make b/contrib/libs/liburing/test/sq-full.t/ya.make
index c3cfd2e875e..cf5dea0b181 100644
--- a/contrib/libs/liburing/test/sq-full.t/ya.make
+++ b/contrib/libs/liburing/test/sq-full.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sq-poll-dup.t/ya.make b/contrib/libs/liburing/test/sq-poll-dup.t/ya.make
index 13f30f3b269..2f9c7fe02e1 100644
--- a/contrib/libs/liburing/test/sq-poll-dup.t/ya.make
+++ b/contrib/libs/liburing/test/sq-poll-dup.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sq-poll-kthread.t/ya.make b/contrib/libs/liburing/test/sq-poll-kthread.t/ya.make
index ec9b4cff79d..f314b2cf511 100644
--- a/contrib/libs/liburing/test/sq-poll-kthread.t/ya.make
+++ b/contrib/libs/liburing/test/sq-poll-kthread.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sq-poll-share.t/ya.make b/contrib/libs/liburing/test/sq-poll-share.t/ya.make
index 9b78ea174b7..82b24ae86d3 100644
--- a/contrib/libs/liburing/test/sq-poll-share.t/ya.make
+++ b/contrib/libs/liburing/test/sq-poll-share.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sq-space_left.t/ya.make b/contrib/libs/liburing/test/sq-space_left.t/ya.make
index 48c83bb9d5b..d3be5024eca 100644
--- a/contrib/libs/liburing/test/sq-space_left.t/ya.make
+++ b/contrib/libs/liburing/test/sq-space_left.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sqe-mixed-bad-wrap.t/ya.make b/contrib/libs/liburing/test/sqe-mixed-bad-wrap.t/ya.make
index c0ab93b3278..a1bf4596481 100644
--- a/contrib/libs/liburing/test/sqe-mixed-bad-wrap.t/ya.make
+++ b/contrib/libs/liburing/test/sqe-mixed-bad-wrap.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sqe-mixed-boundary.c b/contrib/libs/liburing/test/sqe-mixed-boundary.c
new file mode 100644
index 00000000000..8819f1e3c7f
--- /dev/null
+++ b/contrib/libs/liburing/test/sqe-mixed-boundary.c
@@ -0,0 +1,185 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test SQE_MIXED physical SQE boundary validation with sq_array
+ *
+ * Verify that 128-byte operations are correctly rejected when sq_array
+ * remaps them to the last physical SQE slot, preventing a 64-byte OOB
+ * read past the SQE array.
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "test.h"
+
+#define NENTRIES 4
+
+/*
+ * Positive test: NOP128 at a valid physical position should succeed.
+ */
+static int test_valid_position(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { .flags = IORING_SETUP_SQE_MIXED };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret;
+
+ ret = t_io_uring_init_sqarray(NENTRIES, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "ring init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe128(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get_sqe128 failed\n");
+ goto fail;
+ }
+ io_uring_prep_nop128(sqe);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret < 0) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto fail;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ goto fail;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ goto fail;
+ }
+ if (cqe->user_data == 2 && cqe->res != 0) {
+ fprintf(stderr, "NOP128 at valid position failed: %d\n",
+ cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto fail;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+fail:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * Negative test: NOP128 at the last physical SQE slot via sq_array remap
+ * must be rejected. Without the kernel fix, this triggers a 64-byte OOB
+ * read in io_uring_cmd_sqe_copy().
+ */
+static int test_oob_boundary(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { .flags = IORING_SETUP_SQE_MIXED };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ unsigned mask;
+ int ret, i, found;
+
+ ret = t_io_uring_init_sqarray(NENTRIES, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "ring init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ mask = *ring.sq.kring_entries - 1;
+
+ /* Advance internal tail: NOP (1) + NOP128 (2) = 3 slots */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe128(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get_sqe128 failed\n");
+ goto fail;
+ }
+
+ /*
+ * Override: remap logical position 1 to last physical slot.
+ * Prep NOP128 there instead of the position get_sqe128 returned.
+ */
+ ring.sq.array[1] = mask;
+ memset(&ring.sq.sqes[mask], 0, sizeof(struct io_uring_sqe));
+ io_uring_prep_nop128(&ring.sq.sqes[mask]);
+ ring.sq.sqes[mask].user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret < 0) {
+ fprintf(stderr, "submit: %d\n", ret);
+ goto fail;
+ }
+
+ found = 0;
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret)
+ break;
+ if (cqe->user_data == 2) {
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr,
+ "NOP128 at last slot: expected -EINVAL, got %d\n",
+ cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+ goto fail;
+ }
+ found = 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ if (!found) {
+ fprintf(stderr, "no CQE for NOP128 boundary test\n");
+ goto fail;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+fail:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_valid_position();
+ if (ret == T_EXIT_SKIP)
+ return T_EXIT_SKIP;
+ if (ret) {
+ fprintf(stderr, "test_valid_position failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_oob_boundary();
+ if (ret) {
+ fprintf(stderr, "test_oob_boundary failed\n");
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/sqe-mixed-boundary.t/ya.make b/contrib/libs/liburing/test/sqe-mixed-boundary.t/ya.make
new file mode 100644
index 00000000000..545b39b6502
--- /dev/null
+++ b/contrib/libs/liburing/test/sqe-mixed-boundary.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ sqe-mixed-boundary.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/sqe-mixed-nop.t/ya.make b/contrib/libs/liburing/test/sqe-mixed-nop.t/ya.make
index 8751b410a16..31ad371e7f4 100644
--- a/contrib/libs/liburing/test/sqe-mixed-nop.t/ya.make
+++ b/contrib/libs/liburing/test/sqe-mixed-nop.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sqe-mixed-uring_cmd.t/ya.make b/contrib/libs/liburing/test/sqe-mixed-uring_cmd.t/ya.make
index f63bc90fd1b..c8072fe56ae 100644
--- a/contrib/libs/liburing/test/sqe-mixed-uring_cmd.t/ya.make
+++ b/contrib/libs/liburing/test/sqe-mixed-uring_cmd.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sqpoll-disable-exit.t/ya.make b/contrib/libs/liburing/test/sqpoll-disable-exit.t/ya.make
index 763d45d7889..e38c1ff3dd0 100644
--- a/contrib/libs/liburing/test/sqpoll-disable-exit.t/ya.make
+++ b/contrib/libs/liburing/test/sqpoll-disable-exit.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sqpoll-exec.t/ya.make b/contrib/libs/liburing/test/sqpoll-exec.t/ya.make
index f24d92f8125..f12f6b12897 100644
--- a/contrib/libs/liburing/test/sqpoll-exec.t/ya.make
+++ b/contrib/libs/liburing/test/sqpoll-exec.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sqpoll-exit-hang.t/ya.make b/contrib/libs/liburing/test/sqpoll-exit-hang.t/ya.make
index d294825ac0d..b28b4f812bb 100644
--- a/contrib/libs/liburing/test/sqpoll-exit-hang.t/ya.make
+++ b/contrib/libs/liburing/test/sqpoll-exit-hang.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sqpoll-sleep.t/ya.make b/contrib/libs/liburing/test/sqpoll-sleep.t/ya.make
index 1781215cb73..e5a641c32ca 100644
--- a/contrib/libs/liburing/test/sqpoll-sleep.t/ya.make
+++ b/contrib/libs/liburing/test/sqpoll-sleep.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sqwait.t/ya.make b/contrib/libs/liburing/test/sqwait.t/ya.make
index 01fc099978d..8a2a6c9cea2 100644
--- a/contrib/libs/liburing/test/sqwait.t/ya.make
+++ b/contrib/libs/liburing/test/sqwait.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/stdout.t/ya.make b/contrib/libs/liburing/test/stdout.t/ya.make
index a2a1c804645..537bbe6fb32 100644
--- a/contrib/libs/liburing/test/stdout.t/ya.make
+++ b/contrib/libs/liburing/test/stdout.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/submit-and-wait.t/ya.make b/contrib/libs/liburing/test/submit-and-wait.t/ya.make
index 6802c30bc9b..e78305c06e7 100644
--- a/contrib/libs/liburing/test/submit-and-wait.t/ya.make
+++ b/contrib/libs/liburing/test/submit-and-wait.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/submit-link-fail.t/ya.make b/contrib/libs/liburing/test/submit-link-fail.t/ya.make
index 167fc058e32..bf742848347 100644
--- a/contrib/libs/liburing/test/submit-link-fail.t/ya.make
+++ b/contrib/libs/liburing/test/submit-link-fail.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/submit-reuse.t/ya.make b/contrib/libs/liburing/test/submit-reuse.t/ya.make
index 736f3386e3a..d7da396d303 100644
--- a/contrib/libs/liburing/test/submit-reuse.t/ya.make
+++ b/contrib/libs/liburing/test/submit-reuse.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/symlink.t/ya.make b/contrib/libs/liburing/test/symlink.t/ya.make
index 6484c89329f..13c4cacae8c 100644
--- a/contrib/libs/liburing/test/symlink.t/ya.make
+++ b/contrib/libs/liburing/test/symlink.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/sync-cancel.t/ya.make b/contrib/libs/liburing/test/sync-cancel.t/ya.make
index ec2cc45ef39..27e3cf267b5 100644
--- a/contrib/libs/liburing/test/sync-cancel.t/ya.make
+++ b/contrib/libs/liburing/test/sync-cancel.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/task-restrict.c b/contrib/libs/liburing/test/task-restrict.c
new file mode 100644
index 00000000000..943d64ff185
--- /dev/null
+++ b/contrib/libs/liburing/test/task-restrict.c
@@ -0,0 +1,415 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test per-task io_uring restrictions
+ *
+ * Per-task restrictions are registered via io_uring_register(2) with fd=-1
+ * and IORING_REGISTER_RESTRICTIONS opcode. Once registered, they apply to
+ * all rings created by that task and are inherited across fork.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+static int register_task_restrictions(struct io_uring_restriction *res,
+ unsigned int nr_res)
+{
+ struct {
+ __u16 flags;
+ __u16 nr_res;
+ __u32 resv[3];
+ struct io_uring_restriction restrictions[];
+ } *arg;
+ size_t sz;
+ int ret;
+
+ sz = sizeof(*arg) + nr_res * sizeof(struct io_uring_restriction);
+ arg = calloc(1, sz);
+ if (!arg)
+ return -ENOMEM;
+
+ arg->nr_res = nr_res;
+ memcpy(arg->restrictions, res, nr_res * sizeof(*res));
+
+ ret = __sys_io_uring_register(-1, IORING_REGISTER_RESTRICTIONS, arg, 1);
+ free(arg);
+ return ret;
+}
+
+/*
+ * Test that per-task restrictions restrict SQE ops on newly created rings
+ */
+static int test_task_restrict_sqe_op(void)
+{
+ struct io_uring_restriction res[1];
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+
+ /* Allow only NOP */
+ res[0].opcode = IORING_RESTRICTION_SQE_OP;
+ res[0].sqe_op = IORING_OP_NOP;
+
+ /*
+ * Task restrictions need to be tested in a child process since
+ * once set they can't be removed for the current task.
+ */
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return T_EXIT_FAIL;
+ }
+
+ if (pid == 0) {
+ /* Child: set no_new_privs (required like seccomp) */
+ prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+
+ /* Register task restriction, then create ring */
+ ret = register_task_restrictions(res, 1);
+ if (ret == -EINVAL || ret == -EBADF) {
+ /* Kernel doesn't support per-task restrictions */
+ _exit(T_EXIT_SKIP);
+ }
+ if (ret) {
+ fprintf(stderr, "register task restrictions: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ /* Create a new ring - should inherit task restrictions */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ /* NOP should be allowed */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 1;
+
+ /* Read should be denied */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, 0, NULL, 0, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "submit: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ for (int i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ switch (cqe->user_data) {
+ case 1: /* nop - should succeed */
+ if (cqe->res != 0) {
+ fprintf(stderr, "nop res: %d\n", cqe->res);
+ _exit(T_EXIT_FAIL);
+ }
+ break;
+ case 2: /* read - should be denied */
+ if (cqe->res != -EACCES) {
+ fprintf(stderr, "read res: %d (expected -EACCES)\n",
+ cqe->res);
+ _exit(T_EXIT_FAIL);
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ _exit(T_EXIT_PASS);
+ }
+
+ /* Parent: wait for child */
+ int status;
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status))
+ return T_EXIT_FAIL;
+ return WEXITSTATUS(status);
+}
+
+/*
+ * Test that per-task restrictions are inherited across fork
+ */
+static int test_task_restrict_fork_inherit(void)
+{
+ struct io_uring_restriction res[2];
+ int ret;
+
+ /* Allow only NOP and WRITE */
+ res[0].opcode = IORING_RESTRICTION_SQE_OP;
+ res[0].sqe_op = IORING_OP_NOP;
+ res[1].opcode = IORING_RESTRICTION_SQE_OP;
+ res[1].sqe_op = IORING_OP_WRITE;
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return T_EXIT_FAIL;
+ }
+
+ if (pid == 0) {
+ /* Child: set no_new_privs (required like seccomp) */
+ prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+
+ /* Register task restriction then fork again */
+ ret = register_task_restrictions(res, 2);
+ if (ret == -EINVAL) {
+ _exit(T_EXIT_SKIP);
+ }
+ if (ret) {
+ fprintf(stderr, "register task restrictions: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ /* Fork a grandchild - restrictions should be inherited */
+ pid_t gpid = fork();
+ if (gpid < 0) {
+ perror("fork");
+ _exit(T_EXIT_FAIL);
+ }
+
+ if (gpid == 0) {
+ /* Grandchild: create ring and verify restrictions */
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "grandchild ring setup: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ /* NOP should be allowed */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 1;
+
+ /* Read should be denied */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, 0, NULL, 0, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "grandchild submit: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ for (int i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "grandchild wait: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != 0) {
+ fprintf(stderr, "grandchild nop: %d\n",
+ cqe->res);
+ _exit(T_EXIT_FAIL);
+ }
+ break;
+ case 2:
+ if (cqe->res != -EACCES) {
+ fprintf(stderr, "grandchild read: %d\n",
+ cqe->res);
+ _exit(T_EXIT_FAIL);
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ _exit(T_EXIT_PASS);
+ }
+
+ int status;
+ waitpid(gpid, &status, 0);
+ if (!WIFEXITED(status))
+ _exit(T_EXIT_FAIL);
+ _exit(WEXITSTATUS(status));
+ }
+
+ int status;
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status))
+ return T_EXIT_FAIL;
+ return WEXITSTATUS(status);
+}
+
+/*
+ * Test that registering task restrictions twice fails
+ */
+static int test_task_restrict_double_register(void)
+{
+ struct io_uring_restriction res[1];
+ int ret;
+
+ res[0].opcode = IORING_RESTRICTION_SQE_OP;
+ res[0].sqe_op = IORING_OP_NOP;
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return T_EXIT_FAIL;
+ }
+
+ if (pid == 0) {
+ prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+
+ ret = register_task_restrictions(res, 1);
+ if (ret == -EINVAL) {
+ _exit(T_EXIT_SKIP);
+ }
+ if (ret) {
+ fprintf(stderr, "first register: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ /* Second registration should fail with -EPERM */
+ ret = register_task_restrictions(res, 1);
+ if (ret != -EPERM) {
+ fprintf(stderr, "second register: %d (expected -EPERM)\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ _exit(T_EXIT_PASS);
+ }
+
+ int status;
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status))
+ return T_EXIT_FAIL;
+ return WEXITSTATUS(status);
+}
+
+/*
+ * Test per-task register op restrictions
+ */
+static int test_task_restrict_register_op(void)
+{
+ struct io_uring_restriction res[1];
+ int ret;
+
+ /* Allow only IORING_REGISTER_FILES */
+ res[0].opcode = IORING_RESTRICTION_REGISTER_OP;
+ res[0].register_op = IORING_REGISTER_FILES;
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return T_EXIT_FAIL;
+ }
+
+ if (pid == 0) {
+ prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+
+ ret = register_task_restrictions(res, 1);
+ if (ret == -EINVAL) {
+ _exit(T_EXIT_SKIP);
+ }
+ if (ret) {
+ fprintf(stderr, "register task restrictions: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ struct io_uring ring;
+ int pipe1[2];
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ _exit(T_EXIT_FAIL);
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ /* Register files should be allowed */
+ ret = io_uring_register_files(&ring, pipe1, 2);
+ if (ret) {
+ fprintf(stderr, "register files: %d\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ /* Register buffers should be denied */
+ uint64_t ptr;
+ struct iovec vec = { .iov_base = &ptr, .iov_len = sizeof(ptr) };
+ ret = io_uring_register_buffers(&ring, &vec, 1);
+ if (ret != -EACCES) {
+ fprintf(stderr, "register buffers: %d (expected -EACCES)\n", ret);
+ _exit(T_EXIT_FAIL);
+ }
+
+ io_uring_queue_exit(&ring);
+ close(pipe1[0]);
+ close(pipe1[1]);
+ _exit(T_EXIT_PASS);
+ }
+
+ int status;
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status))
+ return T_EXIT_FAIL;
+ return WEXITSTATUS(status);
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_task_restrict_sqe_op();
+ if (ret == T_EXIT_SKIP) {
+ printf("Per-task restrictions not supported, skipping\n");
+ return T_EXIT_SKIP;
+ } else if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_task_restrict_sqe_op failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_task_restrict_fork_inherit();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_task_restrict_fork_inherit failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_task_restrict_double_register();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_task_restrict_double_register failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_task_restrict_register_op();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_task_restrict_register_op failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/task-restrict.t/ya.make b/contrib/libs/liburing/test/task-restrict.t/ya.make
new file mode 100644
index 00000000000..55d3cbbdd0d
--- /dev/null
+++ b/contrib/libs/liburing/test/task-restrict.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ task-restrict.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/teardowns.t/ya.make b/contrib/libs/liburing/test/teardowns.t/ya.make
index 74417be900b..9ec4207d2d1 100644
--- a/contrib/libs/liburing/test/teardowns.t/ya.make
+++ b/contrib/libs/liburing/test/teardowns.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/thread-exit.t/ya.make b/contrib/libs/liburing/test/thread-exit.t/ya.make
index 1c265824524..3f0df1a79a1 100644
--- a/contrib/libs/liburing/test/thread-exit.t/ya.make
+++ b/contrib/libs/liburing/test/thread-exit.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/timens-abs-timer.c b/contrib/libs/liburing/test/timens-abs-timer.c
new file mode 100644
index 00000000000..c03dba7e6c3
--- /dev/null
+++ b/contrib/libs/liburing/test/timens-abs-timer.c
@@ -0,0 +1,316 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: regression test for IORING_TIMEOUT_ABS and
+ * IORING_ENTER_ABS_TIMER honouring the submitter's time
+ * namespace. The kernel converts user supplied absolute time
+ * from the caller's time namespace view to host view via
+ * timens_ktime_to_host(). Without that conversion an absolute
+ * deadline submitted from inside a CLONE_NEWTIME namespace fires
+ * immediately instead of after the requested interval.
+ *
+ * The test forks a child, enters a fresh user namespace plus
+ * time namespace with a -10s monotonic offset, submits an
+ * absolute deadline of now + 1s on each path, and asserts the
+ * call returns after ~1s rather than after <100ms. The test is
+ * skipped if the kernel lacks CLONE_NEWTIME support or the
+ * caller cannot create a user namespace.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "helpers.h"
+#include "liburing.h"
+#include "../src/syscall.h"
+
+#ifndef CLONE_NEWTIME
+#define CLONE_NEWTIME 0x00000080
+#endif
+
+#define EXPECTED_NS 1000000000ULL /* deadline at now + 1s */
+#define MIN_OBSERVED_NS 900000000ULL /* fire no earlier than 0.9s */
+#define BUG_OBSERVED_NS 100000000ULL /* bug fires under 0.1s */
+
+static int write_one(const char *path, const char *buf)
+{
+ int fd, ret;
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0)
+ return -errno;
+ ret = write(fd, buf, strlen(buf));
+ close(fd);
+ if (ret < 0)
+ return -errno;
+ if ((size_t) ret != strlen(buf))
+ return -EIO;
+ return 0;
+}
+
+static int enter_unpriv_userns_timens(void)
+{
+ int ret;
+
+ ret = unshare(CLONE_NEWUSER | CLONE_NEWTIME);
+ if (ret < 0)
+ return -errno;
+
+ if (write_one("/proc/self/setgroups", "deny") < 0)
+ return -errno;
+ if (write_one("/proc/self/uid_map", "0 0 1\n") < 0)
+ return -errno;
+ if (write_one("/proc/self/gid_map", "0 0 1\n") < 0)
+ return -errno;
+
+ /* -10s monotonic offset: host_monotonic - 10s inside this ns. */
+ if (write_one("/proc/self/timens_offsets", "monotonic -10 0\n") < 0)
+ return -errno;
+
+ return 0;
+}
+
+static unsigned long long ts_to_ns(const struct timespec *ts)
+{
+ return ts->tv_sec * 1000000000ULL + ts->tv_nsec;
+}
+
+static long long elapsed_ns(const struct timespec *start)
+{
+ struct timespec now;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
+ return -errno;
+ return ts_to_ns(&now) - ts_to_ns(start);
+}
+
+/*
+ * Path 1: IORING_OP_TIMEOUT with IORING_TIMEOUT_ABS, parsed via
+ * io_parse_user_time() in io_uring/timeout.c.
+ */
+static int test_op_timeout_abs(void)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec kts;
+ struct timespec start;
+ struct io_uring ring;
+ long long elapsed;
+ int ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
+ perror("clock_gettime");
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ kts.tv_sec = start.tv_sec + 1;
+ kts.tv_nsec = start.tv_nsec;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_timeout(sqe, &kts, 0, IORING_TIMEOUT_ABS);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ elapsed = elapsed_ns(&start);
+ io_uring_queue_exit(&ring);
+
+ if (elapsed < 0) {
+ fprintf(stderr, "elapsed_ns failed\n");
+ return T_EXIT_FAIL;
+ }
+ if ((unsigned long long) elapsed < BUG_OBSERVED_NS) {
+ fprintf(stderr,
+ "IORING_TIMEOUT_ABS fired after %lld ns, expected ~%llu ns. "
+ "Likely missing timens_ktime_to_host() in io_parse_user_time().\n",
+ elapsed, EXPECTED_NS);
+ return T_EXIT_FAIL;
+ }
+ if ((unsigned long long) elapsed < MIN_OBSERVED_NS) {
+ fprintf(stderr,
+ "IORING_TIMEOUT_ABS fired early at %lld ns\n", elapsed);
+ return T_EXIT_FAIL;
+ }
+ return T_EXIT_PASS;
+}
+
+/*
+ * Path 2: io_uring_enter with IORING_ENTER_ABS_TIMER, parsed
+ * inline in io_uring/wait.c::io_cqring_wait().
+ */
+static int test_enter_abs_timer(void)
+{
+ struct io_uring_getevents_arg arg;
+ struct __kernel_timespec kts;
+ struct timespec start;
+ struct io_uring ring;
+ long long elapsed;
+ int ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
+ perror("clock_gettime");
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+ }
+
+ kts.tv_sec = start.tv_sec + 1;
+ kts.tv_nsec = start.tv_nsec;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.sigmask_sz = _NSIG / 8;
+ arg.ts = (unsigned long) &kts;
+
+ ret = io_uring_enter2(ring.ring_fd, 0, 1,
+ IORING_ENTER_GETEVENTS |
+ IORING_ENTER_EXT_ARG |
+ IORING_ENTER_ABS_TIMER,
+ &arg, sizeof(arg));
+ if (ret != -ETIME) {
+ fprintf(stderr,
+ "io_uring_enter2 returned %d, expected -ETIME (%d)\n",
+ ret, -ETIME);
+ io_uring_queue_exit(&ring);
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ return T_EXIT_FAIL;
+ }
+
+ elapsed = elapsed_ns(&start);
+ io_uring_queue_exit(&ring);
+
+ if (elapsed < 0) {
+ fprintf(stderr, "elapsed_ns failed\n");
+ return T_EXIT_FAIL;
+ }
+ if ((unsigned long long) elapsed < BUG_OBSERVED_NS) {
+ fprintf(stderr,
+ "IORING_ENTER_ABS_TIMER fired after %lld ns, expected ~%llu ns. "
+ "Likely missing timens_ktime_to_host() on the ABS_TIMER branch.\n",
+ elapsed, EXPECTED_NS);
+ return T_EXIT_FAIL;
+ }
+ if ((unsigned long long) elapsed < MIN_OBSERVED_NS) {
+ fprintf(stderr,
+ "IORING_ENTER_ABS_TIMER fired early at %lld ns\n", elapsed);
+ return T_EXIT_FAIL;
+ }
+ return T_EXIT_PASS;
+}
+
+/*
+ * Run the actual io_uring tests inside the new time namespace.
+ * unshare(CLONE_NEWTIME) does not move the caller into the new
+ * namespace, only its future children. So the caller sets up
+ * userns and timens, writes the offset, then forks once more to
+ * enter the new time namespace.
+ */
+static int run_tests_in_timens_grandchild(void)
+{
+ struct timespec probe;
+ int ret;
+
+ /*
+ * Sanity check: clock_gettime should reflect the -10s offset.
+ * If it does not, the offset was not applied and the test
+ * would silently appear to pass on an unpatched kernel.
+ */
+ if (clock_gettime(CLOCK_MONOTONIC, &probe) < 0) {
+ perror("clock_gettime");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_op_timeout_abs();
+ if (ret != T_EXIT_PASS)
+ return ret;
+
+ return test_enter_abs_timer();
+}
+
+static int run_in_timens(void)
+{
+ pid_t pid;
+ int status, ret;
+
+ ret = enter_unpriv_userns_timens();
+ if (ret == -EPERM || ret == -ENOSPC || ret == -EINVAL || ret == -ENOENT)
+ return T_EXIT_SKIP;
+ if (ret) {
+ fprintf(stderr, "userns/timens setup: %s\n", strerror(-ret));
+ return T_EXIT_SKIP;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork (timens)");
+ return T_EXIT_FAIL;
+ }
+ if (pid == 0)
+ _exit(run_tests_in_timens_grandchild());
+
+ if (waitpid(pid, &status, 0) < 0) {
+ perror("waitpid (timens)");
+ return T_EXIT_FAIL;
+ }
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ pid_t pid;
+ int status;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return T_EXIT_FAIL;
+ }
+ if (pid == 0)
+ _exit(run_in_timens());
+
+ if (waitpid(pid, &status, 0) < 0) {
+ perror("waitpid");
+ return T_EXIT_FAIL;
+ }
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/timens-abs-timer.t/ya.make b/contrib/libs/liburing/test/timens-abs-timer.t/ya.make
new file mode 100644
index 00000000000..5b222cb17d4
--- /dev/null
+++ b/contrib/libs/liburing/test/timens-abs-timer.t/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+WITHOUT_LICENSE_TEXTS()
+
+VERSION(2.15)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/liburing
+)
+
+ADDINCL(
+ contrib/libs/liburing/src/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DLIBURING_BUILD_TEST
+ -D__SANE_USERSPACE_TYPES__
+)
+
+SRCDIR(contrib/libs/liburing/test)
+
+SRCS(
+ helpers.c
+ timens-abs-timer.c
+)
+
+END()
diff --git a/contrib/libs/liburing/test/timeout-new.t/ya.make b/contrib/libs/liburing/test/timeout-new.t/ya.make
index 72d03d03c2e..13cc1e97001 100644
--- a/contrib/libs/liburing/test/timeout-new.t/ya.make
+++ b/contrib/libs/liburing/test/timeout-new.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/timeout.c b/contrib/libs/liburing/test/timeout.c
index 6222df79d98..f71772738e2 100644
--- a/contrib/libs/liburing/test/timeout.c
+++ b/contrib/libs/liburing/test/timeout.c
@@ -24,6 +24,7 @@
static int not_supported;
static int no_modify;
static int no_multishot;
+static int no_immediate;
static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
{
@@ -31,11 +32,26 @@ static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
ts->tv_nsec = (msec % 1000) * 1000000;
}
+static void t_prep_timeout(struct io_uring_sqe *sqe,
+ const struct __kernel_timespec *ts,
+ unsigned flags,
+ bool immediate)
+{
+ if (!immediate) {
+ io_uring_prep_timeout(sqe, ts, 0, 0);
+ } else {
+ io_uring_prep_timeout(sqe, NULL, 0, 0);
+ sqe->addr = ts->tv_sec * 1000000000 + ts->tv_nsec;
+ sqe->timeout_flags = IORING_TIMEOUT_IMMEDIATE_ARG;
+ }
+ sqe->timeout_flags |= flags;
+}
+
/*
* Test that we return to userspace if a timeout triggers, even if we
* don't satisfy the number of events asked for.
*/
-static int test_single_timeout_many(struct io_uring *ring)
+static int test_single_timeout_many(struct io_uring *ring, bool immediate)
{
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
@@ -51,7 +67,7 @@ static int test_single_timeout_many(struct io_uring *ring)
}
msec_to_ts(&ts, TIMEOUT_MSEC);
- io_uring_prep_timeout(sqe, &ts, 0, 0);
+ t_prep_timeout(sqe, &ts, 0, immediate);
ret = io_uring_submit(ring);
if (ret <= 0) {
@@ -220,7 +236,7 @@ err:
/*
* Test single timeout waking us up
*/
-static int test_single_timeout(struct io_uring *ring)
+static int test_single_timeout(struct io_uring *ring, bool immediate)
{
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
@@ -236,7 +252,7 @@ static int test_single_timeout(struct io_uring *ring)
}
msec_to_ts(&ts, TIMEOUT_MSEC);
- io_uring_prep_timeout(sqe, &ts, 0, 0);
+ t_prep_timeout(sqe, &ts, 0, immediate);
ret = io_uring_submit(ring);
if (ret <= 0) {
@@ -253,6 +269,11 @@ static int test_single_timeout(struct io_uring *ring)
ret = cqe->res;
io_uring_cqe_seen(ring, cqe);
if (ret == -EINVAL) {
+ if (immediate) {
+ no_immediate = true;
+ fprintf(stdout, "%s: Timeout (imm) not supported, ignored\n", __FUNCTION__);
+ return 0;
+ }
fprintf(stdout, "%s: Timeout not supported, ignored\n", __FUNCTION__);
not_supported = 1;
return 0;
@@ -417,7 +438,7 @@ err:
/*
* Test single absolute timeout waking us up
*/
-static int test_single_timeout_abs(struct io_uring *ring)
+static int test_single_timeout_abs(struct io_uring *ring, bool immediate)
{
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
@@ -436,7 +457,7 @@ static int test_single_timeout_abs(struct io_uring *ring)
clock_gettime(CLOCK_MONOTONIC, &abs_ts);
ts.tv_sec = abs_ts.tv_sec + 1;
ts.tv_nsec = abs_ts.tv_nsec;
- io_uring_prep_timeout(sqe, &ts, 0, IORING_TIMEOUT_ABS);
+ t_prep_timeout(sqe, &ts, IORING_TIMEOUT_ABS, immediate);
ret = io_uring_submit(ring);
if (ret <= 0) {
@@ -1766,7 +1787,7 @@ int main(int argc, char *argv[])
ret = io_uring_queue_init(8, &sqpoll_ring, IORING_SETUP_SQPOLL);
sqpoll = !ret;
- ret = test_single_timeout(&ring);
+ ret = test_single_timeout(&ring, false);
if (ret) {
fprintf(stderr, "test_single_timeout failed\n");
return ret;
@@ -1774,18 +1795,32 @@ int main(int argc, char *argv[])
if (not_supported)
return 0;
+ ret = test_single_timeout(&ring, true);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout (imm) failed\n");
+ return ret;
+ }
+
ret = test_multi_timeout(&ring);
if (ret) {
fprintf(stderr, "test_multi_timeout failed\n");
return ret;
}
- ret = test_single_timeout_abs(&ring);
+ ret = test_single_timeout_abs(&ring, false);
if (ret) {
fprintf(stderr, "test_single_timeout_abs failed\n");
return ret;
}
+ if (!no_immediate) {
+ ret = test_single_timeout_abs(&ring, true);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_abs (imm) failed\n");
+ return ret;
+ }
+ }
+
ret = test_single_timeout_remove(&ring);
if (ret) {
fprintf(stderr, "test_single_timeout_remove failed\n");
@@ -1798,12 +1833,20 @@ int main(int argc, char *argv[])
return ret;
}
- ret = test_single_timeout_many(&ring);
+ ret = test_single_timeout_many(&ring, false);
if (ret) {
fprintf(stderr, "test_single_timeout_many failed\n");
return ret;
}
+ if (!no_immediate) {
+ ret = test_single_timeout_many(&ring, true);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_many (imm) failed\n");
+ return ret;
+ }
+ }
+
ret = test_single_timeout_nr(&ring, 1);
if (ret) {
fprintf(stderr, "test_single_timeout_nr(1) failed\n");
diff --git a/contrib/libs/liburing/test/timeout.t/ya.make b/contrib/libs/liburing/test/timeout.t/ya.make
index 446f8522558..5056f38408b 100644
--- a/contrib/libs/liburing/test/timeout.t/ya.make
+++ b/contrib/libs/liburing/test/timeout.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/timerfd-short-read.t/ya.make b/contrib/libs/liburing/test/timerfd-short-read.t/ya.make
index ad27fe9f86b..13a650b4334 100644
--- a/contrib/libs/liburing/test/timerfd-short-read.t/ya.make
+++ b/contrib/libs/liburing/test/timerfd-short-read.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/timestamp-bug.c b/contrib/libs/liburing/test/timestamp-bug.c
index d6451a961f4..44cca0283c5 100644
--- a/contrib/libs/liburing/test/timestamp-bug.c
+++ b/contrib/libs/liburing/test/timestamp-bug.c
@@ -24,11 +24,6 @@
#include "liburing.h"
#include "helpers.h"
-/* Compatibility with slightly older kernel headers */
-#ifndef SOCKET_URING_OP_TX_TIMESTAMP
-#define SOCKET_URING_OP_TX_TIMESTAMP 4
-#endif
-
/*
* Create a socket whose error queue contains both timestamp information and
* actual errors.
diff --git a/contrib/libs/liburing/test/timestamp-bug.t/ya.make b/contrib/libs/liburing/test/timestamp-bug.t/ya.make
index 71a10319904..b3c488ba7ce 100644
--- a/contrib/libs/liburing/test/timestamp-bug.t/ya.make
+++ b/contrib/libs/liburing/test/timestamp-bug.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/timestamp.t/ya.make b/contrib/libs/liburing/test/timestamp.t/ya.make
index 399017f9237..7e314fbd726 100644
--- a/contrib/libs/liburing/test/timestamp.t/ya.make
+++ b/contrib/libs/liburing/test/timestamp.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/truncate.t/ya.make b/contrib/libs/liburing/test/truncate.t/ya.make
index 7a6106b28bc..0f0476378fe 100644
--- a/contrib/libs/liburing/test/truncate.t/ya.make
+++ b/contrib/libs/liburing/test/truncate.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/tty-write-dpoll.t/ya.make b/contrib/libs/liburing/test/tty-write-dpoll.t/ya.make
index 35776e121fa..276b851fa68 100644
--- a/contrib/libs/liburing/test/tty-write-dpoll.t/ya.make
+++ b/contrib/libs/liburing/test/tty-write-dpoll.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/unlink.t/ya.make b/contrib/libs/liburing/test/unlink.t/ya.make
index 1715f77ae14..29a30984ea0 100644
--- a/contrib/libs/liburing/test/unlink.t/ya.make
+++ b/contrib/libs/liburing/test/unlink.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/vec-regbuf.t/ya.make b/contrib/libs/liburing/test/vec-regbuf.t/ya.make
index ed5e4a6471c..a98a9f270fa 100644
--- a/contrib/libs/liburing/test/vec-regbuf.t/ya.make
+++ b/contrib/libs/liburing/test/vec-regbuf.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/version.t/ya.make b/contrib/libs/liburing/test/version.t/ya.make
index 78da9f9d025..84c17bae65d 100644
--- a/contrib/libs/liburing/test/version.t/ya.make
+++ b/contrib/libs/liburing/test/version.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/wait-timeout.t/ya.make b/contrib/libs/liburing/test/wait-timeout.t/ya.make
index 8f24e66e42e..a4adc52aaf2 100644
--- a/contrib/libs/liburing/test/wait-timeout.t/ya.make
+++ b/contrib/libs/liburing/test/wait-timeout.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/waitid.t/ya.make b/contrib/libs/liburing/test/waitid.t/ya.make
index fcd2461fd7d..fff6b1d4d15 100644
--- a/contrib/libs/liburing/test/waitid.t/ya.make
+++ b/contrib/libs/liburing/test/waitid.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/wakeup-hang.t/ya.make b/contrib/libs/liburing/test/wakeup-hang.t/ya.make
index 7c030d78450..cac7ca2561f 100644
--- a/contrib/libs/liburing/test/wakeup-hang.t/ya.make
+++ b/contrib/libs/liburing/test/wakeup-hang.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/wq-aff.t/ya.make b/contrib/libs/liburing/test/wq-aff.t/ya.make
index 9e8c6137e15..4bfbb263951 100644
--- a/contrib/libs/liburing/test/wq-aff.t/ya.make
+++ b/contrib/libs/liburing/test/wq-aff.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/xattr.t/ya.make b/contrib/libs/liburing/test/xattr.t/ya.make
index e58201fbb4b..b0590ab1d89 100644
--- a/contrib/libs/liburing/test/xattr.t/ya.make
+++ b/contrib/libs/liburing/test/xattr.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/test/zcrx.c b/contrib/libs/liburing/test/zcrx.c
index f7b9e826058..a73813821c2 100644
--- a/contrib/libs/liburing/test/zcrx.c
+++ b/contrib/libs/liburing/test/zcrx.c
@@ -1,7 +1,7 @@
#include "../config-host.h"
/* SPDX-License-Identifier: MIT */
/*
- * Simple test case showing using send and recv through io_uring
+ * Zero copy receive tests
*/
#include <errno.h>
#include <stdio.h>
@@ -10,6 +10,7 @@
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/mman.h>
+#include <linux/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <pthread.h>
@@ -18,69 +19,133 @@
#include "liburing.h"
#include "helpers.h"
-static unsigned int ifidx, rxq;
+#define RING_FLAGS (IORING_SETUP_DEFER_TASKRUN | \
+ IORING_SETUP_CQE32 | \
+ IORING_SETUP_SINGLE_ISSUER | \
+ IORING_SETUP_SUBMIT_ALL)
+
+#define RQ_ENTRIES 128
+#define RQ_ENTRIES_SMALL 16
+#define AREA_SZ (4096 * 132)
+#define HUGEPAGE_AREA_SZ (16 << 20)
+
+#define T_ALIGN_UP(v, align) (((v) + (align) - 1) & ~((align) - 1))
+
+struct zcrx_reg {
+ struct io_uring_zcrx_area_reg area;
+ struct io_uring_region_desc rq_region;
+ struct io_uring_zcrx_ifq_reg zcrx;
+};
+
+struct t_executor {
+ struct io_uring_zcrx_rq rq;
+ struct io_uring ring;
+ struct zcrx_reg reg;
+ int fds[2];
+};
+static struct io_uring_query_zcrx query;
+static bool zcrx_supported = false;
static long page_size;
-/* the hw rxq must consume 128 of these pages, leaving 4 left */
-#define AREA_PAGES 132
-#define AREA_SZ (AREA_PAGES * page_size)
-#define RQ_ENTRIES 128
-/* this is one more than the # of free pages after filling hw rxq */
-#define LOOP_COUNT 5
-#define DEV_ENV_VAR "NETIF"
-#define RXQ_ENV_VAR "NETRXQ"
-#define RING_FLAGS (IORING_SETUP_DEFER_TASKRUN | \
- IORING_SETUP_CQE32 | \
- IORING_SETUP_SINGLE_ISSUER)
+static void *def_rq_mem;
+static void *def_area_mem;
+static void *def_hugepage_area_mem;
+static void *ro_param_mem;
+static size_t ro_param_mem_size;
+
+enum {
+ CONFIG_HUGEPAGE = 1 << 0,
+ CONFIG_SMALL_RQ = 1 << 1,
+};
-static char str[] = "iv5t4dl500w7wsrf14fsuq8thptto0z7i2q62z1p8dwrv5u4kaxpqhm2rb7bapddi5gfkh7f9695eh46t2o5yap2y43gstbsq3n90bg1i7zx1m4wojoqbuxhsrw4s4y3sh9qp57ovbaa2o9yaqa7d4to2vak1otvgkoxs5t0ovjbe6roginrjeh906kmjn1289jlho9a1bud02ex4xr3cvfcybpl6axnr117p0aesb3070wlvj91en7tpf8nyb1e";
+static void *write_ro_params(void *src, size_t bytes)
+{
+ int ret;
-#define MSG_SIZE 512
+ if (bytes > ro_param_mem_size)
+ t_error(0, 1, "write_to_ro: too large");
+ ret = mprotect(ro_param_mem, ro_param_mem_size, PROT_READ | PROT_WRITE);
+ if (ret)
+ t_error(0, errno, "mprotect failed");
-#define PORT 10202
-#define HOST "127.0.0.1"
+ memcpy(ro_param_mem, src, bytes);
-static int probe_zcrx(void *area)
+ ret = mprotect(ro_param_mem, ro_param_mem_size, PROT_READ);
+ if (ret)
+ t_error(0, errno, "mprotect read failed");
+
+ return ro_param_mem;
+}
+
+static struct io_uring_cqe *submit_and_wait_one(struct io_uring *ring)
{
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(area),
- .len = AREA_SZ,
- .flags = 0,
- };
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1)
+ t_error(1, ret, "submit_and_wait_one: submit fail\n");
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0)
+ t_error(1, ret, "submit_and_wait_one: wait fail\n");
+
+ return cqe;
+}
+
+static void test_io_uring_prep_zcrx(struct io_uring_sqe *sqe, int fd, int zcrx_id)
+{
+ io_uring_prep_rw(IORING_OP_RECV_ZC, sqe, fd, NULL, 0, 0);
+ sqe->zcrx_ifq_idx = zcrx_id;
+ sqe->ioprio |= IORING_RECV_MULTISHOT;
+}
+
+static bool rq_ctrl_op_supported(int op)
+{
+ return query.nr_ctrl_opcodes > op;
+}
+
+static int t_zcrx_ctrl(struct io_uring *ring, struct zcrx_ctrl *ctrl)
+{
+ return io_uring_register(ring->ring_fd, IORING_REGISTER_ZCRX_CTRL, ctrl, 0);
+}
+
+static void query_zcrx(void)
+{
+ struct io_uring_query_hdr hdr = {
+ .size = sizeof(query),
+ .query_data = uring_ptr_to_u64(&query),
+ .query_op = IO_URING_QUERY_ZCRX,
};
- struct io_uring ring;
int ret;
- ret = t_create_ring(8, &ring, RING_FLAGS);
- if (ret == T_SETUP_SKIP) {
- fprintf(stderr, "required ring flags are not supported, skip\n");
- return T_EXIT_SKIP;
- }
- if (ret) {
- fprintf(stderr, "probe ring setup failure\n");
- return T_EXIT_FAIL;
+ ret = io_uring_register(-1, IORING_REGISTER_QUERY, &hdr, 0);
+ if (ret < 0 || hdr.result < 0) {
+ memset(&query, 0, sizeof(query));
+ query.rq_hdr_size = page_size;
+ query.rq_hdr_alignment = page_size;
+ } else {
+ zcrx_supported = true;
}
+}
- ret = io_uring_register_ifq(&ring, &reg);
- if (ret == -EINVAL) {
- fprintf(stderr, "zcrx is not supported, skip\n");
- return T_EXIT_SKIP;
- }
- if (ret) {
- fprintf(stderr, "probe zcrx register fail %i\n", ret);
- return T_EXIT_FAIL;
- }
- io_uring_queue_exit(&ring);
- return T_EXIT_PASS;
+static unsigned rq_nr_queued(struct io_uring_zcrx_rq *rq)
+{
+ return rq->rq_tail - io_uring_smp_load_acquire(rq->khead);
+}
+
+static int flush_rq(struct io_uring *ring, int zcrx_id)
+{
+ struct zcrx_ctrl ctrl = {
+ .zcrx_id = zcrx_id,
+ .op = ZCRX_CTRL_FLUSH_RQ,
+ };
+
+ return t_zcrx_ctrl(ring, &ctrl);
}
-static int try_register_ifq(struct io_uring_zcrx_ifq_reg *reg)
+static int try_register_zcrx(struct io_uring_zcrx_ifq_reg *reg)
{
struct io_uring ring;
int ret;
@@ -96,834 +161,1151 @@ static int try_register_ifq(struct io_uring_zcrx_ifq_reg *reg)
return ret;
}
-static int test_invalid_if(void *area)
+static int clone_zcrx(int src_id, struct io_uring *src_ring, struct io_uring *dst_ring,
+ struct io_uring_zcrx_ifq_reg *out_reg)
{
- int ret;
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(area),
- .len = AREA_SZ,
- .flags = 0,
- };
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = -1,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
+ struct zcrx_ctrl export_ctrl;
+ int box_fd, ret;
+
+ export_ctrl = (struct zcrx_ctrl) {
+ .zcrx_id = src_id,
+ .op = ZCRX_CTRL_EXPORT,
};
+ ret = t_zcrx_ctrl(src_ring, &export_ctrl);
+ box_fd = export_ctrl.zc_export.zcrx_fd;
+ if (ret < 0) {
+ fprintf(stderr, "Export failed %i %i\n", ret, box_fd);
+ return ret;
+ }
- ret = try_register_ifq(&reg);
- if (ret != -EINVAL && ret != -ENODEV) {
- fprintf(stderr, "registered invalid IF %i\n", ret);
- return T_EXIT_FAIL;
+ *out_reg = (struct io_uring_zcrx_ifq_reg) {
+ .flags = ZCRX_REG_IMPORT,
+ .if_idx = box_fd,
+ };
+ ret = io_uring_register_ifq(dst_ring, out_reg);
+ if (ret) {
+ fprintf(stderr, "Import failed %i\n", ret);
+ return -1;
}
- reg.if_idx = ifidx;
- reg.if_rxq = -1;
+ close(box_fd);
+ return 0;
+}
- ret = try_register_ifq(&reg);
- if (ret != -EINVAL) {
- fprintf(stderr, "registered invalid IFQ %i\n", ret);
- return T_EXIT_FAIL;
- }
- return T_EXIT_PASS;
+static size_t get_rq_size(unsigned nr_entries)
+{
+ size_t sz;
+
+ sz = T_ALIGN_UP(query.rq_hdr_size, query.rq_hdr_alignment);
+ sz += nr_entries * sizeof(struct io_uring_zcrx_rqe);
+ return T_ALIGN_UP(sz, page_size);
}
-static int test_invalid_ifq_collision(void *area)
+static void default_reg(struct zcrx_reg *reg, unsigned config_flags)
{
- struct io_uring ring, ring2;
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(area),
+ unsigned rq_entries = RQ_ENTRIES;
+
+ if (config_flags & CONFIG_SMALL_RQ)
+ rq_entries = RQ_ENTRIES_SMALL;
+
+ reg->rq_region = (struct io_uring_region_desc) {
+ .size = get_rq_size(rq_entries),
+ .user_addr = uring_ptr_to_u64(def_rq_mem),
+ .flags = IORING_MEM_REGION_TYPE_USER,
+ };
+ reg->area = (struct io_uring_zcrx_area_reg) {
+ .addr = uring_ptr_to_u64(def_area_mem),
.len = AREA_SZ,
.flags = 0,
};
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
+ reg->zcrx = (struct io_uring_zcrx_ifq_reg) {
+ .flags = ZCRX_REG_NODEV,
+ .rq_entries = rq_entries,
+ .area_ptr = uring_ptr_to_u64(&reg->area),
+ .region_ptr = uring_ptr_to_u64(&reg->rq_region),
};
- int ret;
- ret = t_create_ring(8, &ring, RING_FLAGS);
- if (ret != T_SETUP_OK) {
- fprintf(stderr, "ring create failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
- ret = t_create_ring(8, &ring2, RING_FLAGS);
- if (ret != T_SETUP_OK) {
- fprintf(stderr, "ring2 create failed: %d\n", ret);
- return T_EXIT_FAIL;
+ if (config_flags & CONFIG_HUGEPAGE) {
+ reg->area.addr = uring_ptr_to_u64(def_hugepage_area_mem);
+ reg->area.len = HUGEPAGE_AREA_SZ;
}
+}
- ret = io_uring_register_ifq(&ring, &reg);
- if (ret) {
- fprintf(stderr, "initial registration failed %i\n", ret);
- return T_EXIT_FAIL;
- }
+static int test_register_basic(void)
+{
+ struct zcrx_reg reg;
+ int ret;
- /* register taken ifq */
- ret = io_uring_register_ifq(&ring, &reg);
- if (!ret) {
- fprintf(stderr, "registered taken queue\n");
- return T_EXIT_FAIL;
+ default_reg(&reg, 0);
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret == -EPERM)
+ return ret;
+ if (ret) {
+ fprintf(stderr, "default setup failed\n");
+ return ret;
}
- ret = io_uring_register_ifq(&ring2, &reg);
- if (!ret) {
- fprintf(stderr, "registered taken queue ring2\n");
- return T_EXIT_FAIL;
+ if (def_hugepage_area_mem) {
+ default_reg(&reg, CONFIG_HUGEPAGE);
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret) {
+ fprintf(stderr, "default setup+huge failed\n");
+ return ret;
+ }
}
- io_uring_queue_exit(&ring);
- io_uring_queue_exit(&ring2);
- return T_EXIT_PASS;
+ return 0;
}
-static int test_rq_setup(void *area)
+static int test_rq(void)
{
+ struct zcrx_reg reg;
+ unsigned entries;
int ret;
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(area),
- .len = AREA_SZ,
- .flags = 0,
- };
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = 0,
- .area_ptr = uring_ptr_to_u64(&area_reg),
- };
+ default_reg(&reg, 0);
+ reg.zcrx.region_ptr = 0;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered w/o region\n");
+ return ret;
+ }
- ret = try_register_ifq(&reg);
- if (ret != -EINVAL) {
- fprintf(stderr, "registered 0 rq entries\n");
- return T_EXIT_FAIL;
+ default_reg(&reg, 0);
+ reg.rq_region.user_addr = 0;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered rq region with no memory\n");
+ return ret;
}
- reg.rq_entries = (__u32)-1;
+ default_reg(&reg, 0);
+ reg.rq_region.size = 0;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered rq region size=0\n");
+ return ret;
+ }
- ret = try_register_ifq(&reg);
- if (ret != -EINVAL) {
- fprintf(stderr, "registered unlimited nr of rq entries\n");
- return T_EXIT_FAIL;
+ default_reg(&reg, 0);
+ entries = reg.zcrx.rq_entries;
+ reg.zcrx.rq_entries -= 1;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && reg.zcrx.rq_entries != entries) {
+ fprintf(stderr, "registered rq with non pow2 rq\n");
+ return ret;
}
- reg.rq_entries = RQ_ENTRIES - 1;
- ret = try_register_ifq(&reg);
- if (ret != 0) {
- fprintf(stderr, "ifq registration failed %i\n", ret);
- return T_EXIT_FAIL;
+ default_reg(&reg, 0);
+ if (query.rq_hdr_size != page_size && reg.rq_region.size > page_size) {
+ reg.rq_region.size = page_size;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered rq with non pow2 rq\n");
+ return ret;
+ }
}
- if (reg.rq_entries == RQ_ENTRIES - 1) {
- fprintf(stderr, "registered non pow2 refill entries %i\n",
- reg.rq_entries);
- return T_EXIT_FAIL;
+ default_reg(&reg, 0);
+ reg.zcrx.rq_entries = 0;
+ reg.zcrx.rq_entries = ~reg.zcrx.rq_entries;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered rq with unlimited entries\n");
+ return ret;
}
- return T_EXIT_PASS;
+ default_reg(&reg, 0);
+ reg.zcrx.rq_entries += (page_size / sizeof(struct io_uring_zcrx_rqe));
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered rq with too many entries\n");
+ return ret;
+ }
+
+ return 0;
}
-static int test_null_area_reg_struct(void)
+static int test_area(void)
{
+ struct zcrx_reg reg;
int ret;
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(0),
- };
+ default_reg(&reg, 0);
+ reg.area.len = 0;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered area size=0, %i\n", ret);
+ return ret;
+ }
- ret = try_register_ifq(&reg);
- return ret ? T_EXIT_PASS : T_EXIT_FAIL;
-}
+ default_reg(&reg, 0);
+ reg.area.addr = 0;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered with NULL area mem\n");
+ return ret;
+ }
-static int test_null_area(void)
-{
- int ret;
+ default_reg(&reg, 0);
+ reg.zcrx.area_ptr = 0;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -EFAULT) {
+ fprintf(stderr, "registered with no area\n");
+ return ret;
+ }
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(0),
- .len = AREA_SZ,
- .flags = 0,
- };
+ default_reg(&reg, 0);
+ reg.area.addr -= page_size;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EFAULT) {
+ fprintf(stderr, "registered lower than area\n");
+ return ret;
+ }
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
- };
+ default_reg(&reg, 0);
+ reg.area.len += page_size;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EFAULT) {
+ fprintf(stderr, "registered higher than area\n");
+ return ret;
+ }
+
+ default_reg(&reg, 0);
+ reg.area.len -= page_size / 2;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EFAULT && ret != -EINVAL) {
+ fprintf(stderr, "registered unaligned area size\n");
+ return ret;
+ }
- ret = try_register_ifq(&reg);
- return ret ? T_EXIT_PASS : T_EXIT_FAIL;
+ default_reg(&reg, 0);
+ reg.area.len /= 2;
+ reg.area.addr += page_size / 2;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EFAULT && ret != -EINVAL) {
+ fprintf(stderr, "registered unaligned area ptr\n");
+ return ret;
+ }
+
+ return 0;
}
-static int test_misaligned_area(void *area)
+static int test_ro_params(void)
{
+ struct zcrx_reg __reg, *reg;
int ret;
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(area + 1),
- .len = AREA_SZ,
- .flags = 0,
- };
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
- };
+ default_reg(&__reg, 0);
+ reg = write_ro_params(&__reg, sizeof(__reg));
- if (!try_register_ifq(&reg))
- return T_EXIT_FAIL;
-
- area_reg.addr = uring_ptr_to_u64(area);
- area_reg.len = AREA_SZ - 1;
- ret = try_register_ifq(&reg);
- return ret ? T_EXIT_PASS : T_EXIT_FAIL;
+ ret = try_register_zcrx(&reg->zcrx);
+ if (ret != -EFAULT) {
+ fprintf(stderr, "registered unaligned area ptr\n");
+ return ret;
+ }
+ return 0;
}
-static int test_larger_than_alloc_area(void *area)
+static int test_invalid_rx_page(void)
{
+ struct zcrx_reg reg;
int ret;
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(area),
- .len = AREA_SZ + 4096,
- .flags = 0,
- };
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
- };
+ default_reg(&reg, 0);
+ reg.zcrx.rx_buf_len = ~reg.zcrx.rx_buf_len;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -ERANGE && ret != -EOVERFLOW) {
+ fprintf(stderr, "registered UINT_MAX rx buf len\n");
+ return ret;
+ }
+
+ default_reg(&reg, 0);
+ reg.zcrx.rx_buf_len = 1U << 31;
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EINVAL && ret != -ERANGE && ret != -EOVERFLOW && ret != -EOPNOTSUPP) {
+ fprintf(stderr, "registered too_large rx buf len\n");
+ return ret;
+ }
- ret = try_register_ifq(&reg);
- return ret ? T_EXIT_PASS : T_EXIT_FAIL;
+ return 0;
}
-static int test_area_access(void)
+static int __prep_server(struct t_executor *ctx, unsigned config_flags)
{
- struct io_uring_zcrx_area_reg area_reg = {
- .len = AREA_SZ,
- .flags = 0,
- };
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
- };
- int i, ret;
- void *area;
+ struct io_uring_zcrx_ifq_reg *zcrx_reg = &ctx->reg.zcrx;
+ char *rqp;
+ int ret;
- for (i = 0; i < 2; i++) {
- int ro = i & 1;
- int prot = ro ? PROT_READ : PROT_WRITE;
+ ret = t_create_ring(128, &ctx->ring, RING_FLAGS);
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return -1;
+ }
- area = mmap(NULL, AREA_SZ, prot,
- MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
- if (area == MAP_FAILED) {
- perror("mmap");
- return T_EXIT_FAIL;
- }
+ default_reg(&ctx->reg, config_flags);
+ rqp = (char *)(uintptr_t)ctx->reg.rq_region.user_addr;
+ memset(rqp, 0, get_rq_size(0));
- area_reg.addr = uring_ptr_to_u64(area);
+ ret = io_uring_register_ifq(&ctx->ring, zcrx_reg);
+ if (ret) {
+ fprintf(stderr, "Can't register zcrx %i\n", ret);
+ return ret;
+ }
- ret = try_register_ifq(&reg);
- if (ret != -EFAULT) {
- fprintf(stderr, "registered unaccessible memory\n");
- return T_EXIT_FAIL;
- }
+ ctx->rq.khead = (unsigned int *)((char *)rqp + zcrx_reg->offsets.head);
+ ctx->rq.ktail = (unsigned int *)((char *)rqp + zcrx_reg->offsets.tail);
+ ctx->rq.rqes = (struct io_uring_zcrx_rqe *)((char *)rqp + zcrx_reg->offsets.rqes);
+ ctx->rq.rq_tail = 0;
+ ctx->rq.ring_entries = zcrx_reg->rq_entries;
- munmap(area, AREA_SZ);
+ ret = t_create_socket_pair(ctx->fds, true);
+ if (ret) {
+ fprintf(stderr, "t_create_socket_pair failed: %d\n", ret);
+ return ret;
}
- return T_EXIT_PASS;
+ return 0;
}
-static int create_ring_with_ifq(struct io_uring *ring, void *area, __u32 *id)
+static int prep_server(struct t_executor *ctx)
{
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(area),
- .len = AREA_SZ,
- .flags = 0,
- };
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
- };
- int ret;
+ return __prep_server(ctx, 0);
+}
- ret = t_create_ring(128, ring, RING_FLAGS);
- if (ret != T_SETUP_OK) {
- fprintf(stderr, "ring create failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
+static void fill_pattern(char *b, size_t size, unsigned long seq)
+{
+ for (long i = 0; i < size; i++)
+ b[i] = ((seq + i) * 1000000001UL) ^ 0xdeadbeef;
+}
- ret = io_uring_register_ifq(ring, &reg);
- if (ret) {
- io_uring_queue_exit(ring);
- fprintf(stderr, "ifq register failed %d\n", ret);
- return T_EXIT_FAIL;
+static int check_pattern(char *b, size_t size, unsigned long seq)
+{
+ for (long i = 0; i < size; i++) {
+ char exp = ((seq + i) * 1000000001UL) ^ 0xdeadbeef;
+
+ if (exp != b[i])
+ return -3;
}
- *id = reg.zcrx_id;
return 0;
}
-static void test_io_uring_prep_zcrx(struct io_uring_sqe *sqe, int fd, int ifq)
+enum {
+ T_RETURN_BUFS = 0x1,
+ T_RETURN_LAZY = 0x2,
+};
+
+static int return_buffer(struct t_executor *ctx, struct io_uring_cqe *cqe,
+ unsigned flags)
{
- io_uring_prep_rw(IORING_OP_RECV_ZC, sqe, fd, NULL, 0, 0);
- sqe->zcrx_ifq_idx = ifq;
- sqe->ioprio |= IORING_RECV_MULTISHOT;
+ struct io_uring_zcrx_rq *rq = &ctx->rq;
+ const struct io_uring_zcrx_cqe *rcqe = (void *)(cqe + 1);
+ struct io_uring_zcrx_rqe *rqe;
+ unsigned rq_mask;
+ int ret;
+
+ rq_mask = rq->ring_entries - 1;
+ rqe = &rq->rqes[rq->rq_tail & rq_mask];
+ rqe->off = (rcqe->off & ~IORING_ZCRX_AREA_MASK) | ctx->reg.area.rq_area_token;
+ rqe->len = cqe->res;
+ io_uring_smp_store_release(rq->ktail, ++rq->rq_tail);
+
+ if (rq_nr_queued(rq) >= rq->ring_entries || !(flags & T_RETURN_LAZY)) {
+ /* flush eagerly for the test */
+ ret = flush_rq(&ctx->ring, ctx->reg.zcrx.zcrx_id);
+ if (ret) {
+ fprintf(stderr, "RQ flush failed %i\n", ret);
+ return ret;
+ }
+ }
+ return 0;
}
-static struct io_uring_cqe *submit_and_wait_one(struct io_uring *ring)
+static int transfer_bytes(struct t_executor *ctx, size_t length,
+ unsigned flags)
{
struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ ssize_t bytes_tx = 0, bytes_rx = 0;
+ size_t tx_buf_size = page_size;
+ char *tx_buffer;
int ret;
- ret = io_uring_submit(ring);
- if (ret != 1) {
- fprintf(stderr, "sqe submit failed: %d\n", ret);
- return NULL;
+ tx_buffer = t_aligned_alloc(page_size, tx_buf_size);
+ if (!tx_buffer)
+ t_error(1, 0, "Can't alloc tx buffer");
+
+ sqe = io_uring_get_sqe(&ctx->ring);
+ test_io_uring_prep_zcrx(sqe, ctx->fds[0], ctx->reg.zcrx.zcrx_id);
+ sqe->len = length;
+ ret = io_uring_submit(&ctx->ring);
+ if (ret != 1)
+ t_error(1, ret, "transfer_bytes: submit fail\n");
+
+ while (bytes_rx < length) {
+ if (bytes_tx < length && (ssize_t)(bytes_tx - bytes_rx) < 2 * tx_buf_size) {
+ ssize_t to_send = length - bytes_tx;
+ ssize_t sent;
+
+ if (to_send > tx_buf_size)
+ to_send = tx_buf_size;
+ fill_pattern(tx_buffer, to_send, bytes_tx);
+ sent = send(ctx->fds[1], tx_buffer, tx_buf_size, 0);
+ if (sent <= 0)
+ t_error(1, sent, "Send failed\n");
+ bytes_tx += sent;
+ }
+
+ if (bytes_rx > bytes_tx)
+ t_error(1, 0, "unexpected tx/rx state");
+ if (bytes_tx == bytes_rx)
+ continue;
+
+ ret = io_uring_wait_cqe(&ctx->ring, &cqe);
+ if (ret < 0)
+ t_error(1, ret, "submit_and_wait_one: wait fail\n");
+ ret = 0;
+
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ ret = cqe->res < 0 ? cqe->res : -1;
+ } else if (cqe->res < 0) {
+ ret = cqe->res;
+ } else {
+ const struct io_uring_zcrx_cqe *rcqe = (void *)(cqe + 1);
+ __u64 mask = (1ULL << IORING_ZCRX_AREA_SHIFT) - 1;
+ char *data = (char *)(uintptr_t)ctx->reg.area.addr + (rcqe->off & mask);
+
+ if (check_pattern(data, cqe->res, bytes_rx)) {
+ ret = -3;
+ break;
+ }
+ bytes_rx += cqe->res;
+
+ if (flags & T_RETURN_BUFS) {
+ ret = return_buffer(ctx, cqe, flags);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ io_uring_cqe_seen(&ctx->ring, cqe);
+ if (ret < 0)
+ goto done;
}
- ret = io_uring_wait_cqe(ring, &cqe);
- if (ret < 0) {
- fprintf(stderr, "wait completion %d\n", ret);
- return NULL;
+ ret = io_uring_wait_cqe(&ctx->ring, &cqe);
+ if (ret < 0)
+ t_error(1, ret, "submit_and_wait_one: wait fail\n");
+
+ if ((cqe->flags & IORING_CQE_F_MORE) || cqe->res != 0) {
+ ret = -2;
+ goto done;
}
+ io_uring_cqe_seen(&ctx->ring, cqe);
+ ret = 0;
+done:
+ free(tx_buffer);
+ return ret;
+}
- return cqe;
+static void clean_server_noring(struct t_executor *ctx)
+{
+ close(ctx->fds[0]);
+ close(ctx->fds[1]);
}
-static int test_invalid_zcrx_request(void *area)
+static void clean_server(struct t_executor *ctx)
+{
+ io_uring_queue_exit(&ctx->ring);
+ clean_server_noring(ctx);
+}
+
+static int test_invalid_recv(void)
{
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
- struct io_uring ring;
- __u32 zcrx_id;
- int ret, fds[2];
-
- ret = create_ring_with_ifq(&ring, area, &zcrx_id);
- if (ret != T_SETUP_OK) {
- fprintf(stderr, "ifq-ring create failed: %d\n", ret);
- return T_EXIT_FAIL;
- }
+ struct t_executor ctx;
+ unsigned zcrx_id;
+ int ret;
- ret = t_create_socket_pair(fds, true);
- if (ret) {
- fprintf(stderr, "t_create_socket_pair failed: %d\n", ret);
+ ret = prep_server(&ctx);
+ if (ret)
return ret;
- }
-
- /* invalid file */
- sqe = io_uring_get_sqe(&ring);
- test_io_uring_prep_zcrx(sqe, ring.ring_fd, zcrx_id);
+ zcrx_id = ctx.reg.zcrx.zcrx_id;
- cqe = submit_and_wait_one(&ring);
- if (!cqe) {
- fprintf(stderr, "submit_and_wait_one failed\n");
- return T_EXIT_FAIL;
- }
+ sqe = io_uring_get_sqe(&ctx.ring);
+ test_io_uring_prep_zcrx(sqe, -1, zcrx_id);
+ cqe = submit_and_wait_one(&ctx.ring);
if (cqe->flags & IORING_CQE_F_MORE) {
- fprintf(stderr, "unexpected F_MORE for invalid fd\n");
- return T_EXIT_FAIL;
+ fprintf(stderr, "unexpected F_MORE for sockfd=-1\n");
+ return -1;
}
- if (cqe->res != -ENOTSOCK) {
- fprintf(stderr, "zcrx for non-socket file\n");
- return T_EXIT_FAIL;
+ if (cqe->res != -ENOTSOCK && cqe->res != -EBADF) {
+ fprintf(stderr, "received from fd=-1 %i\n", cqe->res);
+ return -1;
}
- io_uring_cqe_seen(&ring, cqe);
+ io_uring_cqe_seen(&ctx.ring, cqe);
- /* invalid ifq idx */
- sqe = io_uring_get_sqe(&ring);
- test_io_uring_prep_zcrx(sqe, fds[0], zcrx_id + 1);
-
- cqe = submit_and_wait_one(&ring);
- if (!cqe) {
- fprintf(stderr, "submit_and_wait_one failed\n");
- return T_EXIT_FAIL;
+ sqe = io_uring_get_sqe(&ctx.ring);
+ test_io_uring_prep_zcrx(sqe, ctx.ring.ring_fd, zcrx_id);
+ cqe = submit_and_wait_one(&ctx.ring);
+ if (cqe->flags & IORING_CQE_F_MORE) {
+ fprintf(stderr, "unexpected F_MORE w/ sockfd=ring_fd\n");
+ return -1;
+ }
+ if (cqe->res != -ENOTSOCK && cqe->res != -EBADF) {
+ fprintf(stderr, "received form ring fd %i\n", cqe->res);
+ return -1;
}
+ io_uring_cqe_seen(&ctx.ring, cqe);
+
+ sqe = io_uring_get_sqe(&ctx.ring);
+ test_io_uring_prep_zcrx(sqe, ctx.fds[0], zcrx_id + 1);
+ cqe = submit_and_wait_one(&ctx.ring);
if (cqe->flags & IORING_CQE_F_MORE) {
- fprintf(stderr, "unexpected F_MORE for invalid fd\n");
- return T_EXIT_FAIL;
+ fprintf(stderr, "unexpected F_MORE for invalid zcrx_id\n");
+ return -1;
}
if (cqe->res != -EINVAL) {
- fprintf(stderr, "zcrx recv with non-existent zcrx ifq\n");
- return T_EXIT_FAIL;
+ fprintf(stderr, "received w/ invalid zcrx_id %i\n", cqe->res);
+ return -1;
}
- io_uring_cqe_seen(&ring, cqe);
+ io_uring_cqe_seen(&ctx.ring, cqe);
- close(fds[0]);
- close(fds[1]);
- io_uring_queue_exit(&ring);
+ clean_server(&ctx);
return 0;
}
-struct recv_data {
- pthread_barrier_t connect;
- pthread_barrier_t startup;
- pthread_barrier_t barrier;
- pthread_barrier_t finish;
-
- int accept_fd;
- char buf[MSG_SIZE];
- void *area;
- void *ring_ptr;
- unsigned int ring_sz;
- struct io_uring_zcrx_rq rq_ring;
-};
-
-static int recv_prep(struct io_uring *ring, struct recv_data *rd, int *sock,
- __u32 zcrx_id)
+static int test_exit_with_inflight(void)
{
- struct sockaddr_in saddr;
+ struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
- int sockfd, ret, val, use_fd;
- socklen_t socklen;
+ struct t_executor ctx;
+ unsigned zcrx_id;
+ int ret;
+
+ ret = prep_server(&ctx);
+ if (ret)
+ return ret;
+ zcrx_id = ctx.reg.zcrx.zcrx_id;
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_addr.s_addr = htonl(INADDR_ANY);
- saddr.sin_port = htons(PORT);
+ sqe = io_uring_get_sqe(&ctx.ring);
+ test_io_uring_prep_zcrx(sqe, ctx.fds[0], zcrx_id);
+ ret = io_uring_submit(&ctx.ring);
+ if (ret != 1)
+ t_error(1, ret, "submit_and_wait_one: submit fail\n");
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0) {
- perror("socket");
- return 1;
+ ret = io_uring_peek_cqe(&ctx.ring, &cqe);
+ if (ret == 0) {
+ fprintf(stderr, "Early terminated request\n");
+ return -1;
}
- val = 1;
- setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ io_uring_queue_exit(&ctx.ring);
+ sleep(1);
+ clean_server_noring(&ctx);
+ return 0;
+}
- ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
- if (ret < 0) {
- perror("bind");
- goto err;
+static int test_zcrx_invalid_clone(void)
+{
+ struct io_uring_zcrx_ifq_reg import;
+ struct io_uring r1, r2;
+ struct zcrx_ctrl ctrl, *pctrl;
+ struct zcrx_reg reg;
+ unsigned box_fd;
+ int ret;
+
+ ret = t_create_ring(128, &r1, RING_FLAGS);
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring1 create failed: %d\n", ret);
+ return -1;
+ }
+ ret = t_create_ring(128, &r2, RING_FLAGS);
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring1 create failed: %d\n", ret);
+ return -1;
}
- ret = listen(sockfd, 1);
- if (ret < 0) {
- perror("listen");
- goto err;
+ ctrl = (struct zcrx_ctrl) {
+ .zcrx_id = 0,
+ .op = ZCRX_CTRL_EXPORT,
+ };
+ ret = io_uring_register(-1, IORING_REGISTER_ZCRX_CTRL, &ctrl, 0);
+ if (!ret) {
+ fprintf(stderr, "exported with no ring %i\n", ret);
+ return -1;
}
- pthread_barrier_wait(&rd->connect);
+ ctrl = (struct zcrx_ctrl) {
+ .zcrx_id = 0,
+ .op = ZCRX_CTRL_EXPORT,
+ };
+ ret = t_zcrx_ctrl(&r1, &ctrl);
+ if (ret == 0) {
+ fprintf(stderr, "exported without zcrx %i\n", ret);
+ return -1;
+ }
- socklen = sizeof(saddr);
- use_fd = accept(sockfd, (struct sockaddr *)&saddr, &socklen);
- if (use_fd < 0) {
- perror("accept");
- goto err;
+ import = (struct io_uring_zcrx_ifq_reg) {
+ .flags = ZCRX_REG_IMPORT,
+ .if_idx = -1,
+ };
+ ret = io_uring_register_ifq(&r1, &import);
+ if (ret == 0) {
+ fprintf(stderr, "register invalid box %i\n", ret);
+ return -1;
}
- rd->accept_fd = use_fd;
- pthread_barrier_wait(&rd->startup);
- pthread_barrier_wait(&rd->barrier);
+ default_reg(&reg, 0);
+ ret = io_uring_register_ifq(&r1, &reg.zcrx);
+ if (ret) {
+ fprintf(stderr, "Can't register zcrx\n");
+ return ret;
+ }
- sqe = io_uring_get_sqe(ring);
- io_uring_prep_rw(IORING_OP_RECV_ZC, sqe, use_fd, NULL, 0, 0);
- sqe->zcrx_ifq_idx = zcrx_id;
- sqe->ioprio |= IORING_RECV_MULTISHOT;
- sqe->user_data = 2;
+ ctrl = (struct zcrx_ctrl) {
+ .zcrx_id = reg.zcrx.zcrx_id,
+ .op = ZCRX_CTRL_EXPORT,
+ };
+ pctrl = write_ro_params(&ctrl, sizeof(ctrl));
+ ret = t_zcrx_ctrl(&r1, pctrl);
+ if (ret == 0) {
+ fprintf(stderr, "exported with ro params\n");
+ return ret;
+ }
- ret = io_uring_submit(ring);
- if (ret <= 0) {
- fprintf(stderr, "submit failed: %d\n", ret);
- goto err;
+ ctrl = (struct zcrx_ctrl) {
+ .zcrx_id = reg.zcrx.zcrx_id,
+ .op = ZCRX_CTRL_EXPORT,
+ };
+ ret = t_zcrx_ctrl(&r1, &ctrl);
+ box_fd = ctrl.zc_export.zcrx_fd;
+ if (ret < 0 || (int)box_fd < 0) {
+ fprintf(stderr, "Can'r export zcrx %i\n", ret);
+ return -1;
+ }
+
+ import = (struct io_uring_zcrx_ifq_reg) {
+ .flags = ZCRX_REG_IMPORT,
+ .if_idx = box_fd,
+ };
+ ret = io_uring_register(-1, IORING_REGISTER_ZCRX_IFQ, &import, 1);
+ if (ret == 0) {
+ fprintf(stderr, "Imported wo ring %i\n", ret);
+ return ret;
}
- *sock = sockfd;
+ close(box_fd);
+ io_uring_queue_exit(&r1);
+ io_uring_queue_exit(&r2);
return 0;
-err:
- close(sockfd);
- return 1;
}
-static struct io_uring_zcrx_rqe* get_refill_entry(struct io_uring_zcrx_rq *rq_ring)
+static int test_zcrx_clone(void)
{
- unsigned mask = rq_ring->ring_entries - 1;
- struct io_uring_zcrx_rqe* rqe;
+ struct io_uring ring_exp, ring_imp;
+ struct io_uring_zcrx_ifq_reg import_reg;
+ struct zcrx_reg reg;
+ int ret;
+
+ ret = t_create_ring(128, &ring_exp, RING_FLAGS);
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring1 create failed: %d\n", ret);
+ return -1;
+ }
+ ret = t_create_ring(128, &ring_imp, RING_FLAGS);
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring2 create failed: %d\n", ret);
+ return -1;
+ }
+
+ default_reg(&reg, 0);
+ ret = io_uring_register_ifq(&ring_exp, &reg.zcrx);
+ if (ret) {
+ fprintf(stderr, "Can't register zcrx\n");
+ return ret;
+ }
+
+ ret = clone_zcrx(reg.zcrx.zcrx_id, &ring_exp, &ring_imp, &import_reg);
+ if (ret)
+ return ret;
- rqe = &rq_ring->rqes[rq_ring->rq_tail & mask];
- rq_ring->rq_tail++;
- return rqe;
+ io_uring_queue_exit(&ring_imp);
+ io_uring_queue_exit(&ring_exp);
+ return 0;
}
-static void refill_garbage(struct recv_data *rd, uint64_t area_token)
+static int test_rq_flush(void)
{
- struct io_uring_zcrx_rq *rq_ring = &rd->rq_ring;
- struct io_uring_zcrx_rqe* rqe;
- int i = 0;
+ struct t_executor ctx;
+ int ret;
- /* invalid area */
- rqe = get_refill_entry(rq_ring);
- rqe->off = (area_token + 1) << IORING_ZCRX_AREA_SHIFT;
- i++;
+ ret = prep_server(&ctx);
+ if (ret)
+ return ret;
- /* invalid area offset */
- rqe = get_refill_entry(rq_ring);
- rqe->off = AREA_SZ | (area_token << IORING_ZCRX_AREA_SHIFT);
- rqe->off += AREA_SZ;
- i++;
+ ret = flush_rq(&ctx.ring, ctx.reg.zcrx.zcrx_id + 1);
+ if (!ret) {
+ fprintf(stderr, "Flushed non-existent zcrx %i\n", ret);
+ return ret;
+ }
- for (; i < rq_ring->ring_entries; i++) {
- rqe = get_refill_entry(rq_ring);
- rqe->off = ((uint64_t)1 << IORING_ZCRX_AREA_SHIFT) - 1;
+ if (rq_ctrl_op_supported(ZCRX_CTRL_FLUSH_RQ)) {
+ ret = flush_rq(&ctx.ring, ctx.reg.zcrx.zcrx_id);
+ if (ret) {
+ fprintf(stderr, "RQ flush failed %i\n", ret);
+ return ret;
+ }
}
- io_uring_smp_store_release(rq_ring->ktail, rq_ring->rq_tail);
+ clean_server(&ctx);
+ return 0;
}
-static int do_recv(struct io_uring *ring, struct recv_data *rd,
- uint64_t refill_area_token)
+static int test_recv(void)
{
- struct io_uring_cqe *cqe;
- struct io_uring_zcrx_cqe *zcqe;
- int i, ret;
+ struct t_executor ctx;
+ int ret;
- refill_garbage(rd, refill_area_token);
+ ret = prep_server(&ctx);
+ if (ret)
+ return ret;
- for (i = 0; i < LOOP_COUNT - 1; i++) {
- uint64_t off, mask = (1ULL << IORING_ZCRX_AREA_SHIFT) - 1;
- void *addr;
+ ret = transfer_bytes(&ctx, page_size, 0);
+ if (ret) {
+ fprintf(stderr, "Transfer page failed %i\n", ret);
+ return ret;
+ }
+
+ ret = transfer_bytes(&ctx, ctx.reg.area.len * 2, 0);
+ if (!ret) {
+ fprintf(stderr, "Exhaust area test failed %i\n", ret);
+ return ret;
+ }
+ clean_server(&ctx);
- ret = io_uring_wait_cqe(ring, &cqe);
+ if (rq_ctrl_op_supported(ZCRX_CTRL_FLUSH_RQ)) {
+ ret = prep_server(&ctx);
+ if (ret)
+ return ret;
+
+ ret = transfer_bytes(&ctx, page_size, T_RETURN_BUFS);
if (ret) {
- fprintf(stdout, "wait_cqe: %d\n", ret);
- return 1;
- }
- if (cqe->res == -EINVAL) {
- fprintf(stdout, "recv not supported, skipping\n");
- goto out;
- }
- if (cqe->res < 0) {
- fprintf(stderr, "failed recv cqe: %d\n", cqe->res);
- goto err;
- }
- if (cqe->res - 1 != strlen(str)) {
- fprintf(stderr, "got wrong length: %d/%d\n", cqe->res,
- (int) strlen(str) + 1);
- goto err;
+ fprintf(stderr, "Transfer page + flush failed %i\n", ret);
+ return ret;
}
- zcqe = (struct io_uring_zcrx_cqe *)(cqe + 1);
- off = zcqe->off & mask;
- addr = (char *) rd->area + off;
- ret = strncmp(str, addr, sizeof(str));
- if (ret != 0) {
- fprintf(stderr, "recv incorrect payload: %s\n", (const char *)addr);
- goto err;
+ ret = transfer_bytes(&ctx, ctx.reg.area.len * 2, T_RETURN_BUFS);
+ if (ret) {
+ fprintf(stderr, "Transfer 2xAREA failed %i\n", ret);
+ return ret;
}
+ clean_server(&ctx);
- io_uring_cqe_seen(ring, cqe);
- }
+ if (AREA_SZ > (RQ_ENTRIES_SMALL + 1) * page_size) {
+ ret = __prep_server(&ctx, CONFIG_SMALL_RQ);
+ if (ret)
+ return ret;
- ret = io_uring_wait_cqe(ring, &cqe);
- if (ret) {
- fprintf(stdout, "wait_cqe: %d\n", ret);
- return 1;
- }
- if (cqe->res != -ENOMEM) {
- fprintf(stdout, "final recv cqe did not return ENOMEM\n");
- goto err;
+ ret = transfer_bytes(&ctx, ctx.reg.area.len * 2, T_RETURN_BUFS | T_RETURN_LAZY);
+ if (ret) {
+ fprintf(stderr, "Transfer lazy return failed %i\n", ret);
+ return ret;
+ }
+ clean_server(&ctx);
+ }
}
-out:
- io_uring_cqe_seen(ring, cqe);
- pthread_barrier_wait(&rd->finish);
return 0;
-err:
- io_uring_cqe_seen(ring, cqe);
- pthread_barrier_wait(&rd->finish);
- return 1;
}
-static void *recv_fn(void *data)
+static int test_abnormal_exit(bool iowq, bool pin_zcrx)
{
- struct recv_data *rd = data;
- struct io_uring_params p = { };
+ struct io_uring_sqe *sqe;
struct io_uring ring;
- int ret, sock;
- struct io_uring_zcrx_area_reg area_reg = {
- .addr = uring_ptr_to_u64(rd->area),
- .len = AREA_SZ,
- .flags = 0,
- };
- struct io_uring_zcrx_ifq_reg reg = {
- .if_idx = ifidx,
- .if_rxq = rxq,
- .rq_entries = RQ_ENTRIES,
- .area_ptr = uring_ptr_to_u64(&area_reg),
- };
+ struct zcrx_reg reg;
+ char buf[16] = {};
+ char *refill_queue_ptr;
+ int ret, fds[2];
+ int box_fd = -1;
- p.flags = RING_FLAGS;
- ret = t_create_ring_params(8, &ring, &p);
- if (ret == T_SETUP_SKIP) {
- ret = 0;
- goto err;
- } else if (ret < 0) {
- goto err;
+ ret = t_create_ring(16, &ring, RING_FLAGS);
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return -1;
+ }
+
+ default_reg(&reg, 0);
+ refill_queue_ptr = (char *)(uintptr_t)reg.rq_region.user_addr;
+ memset(refill_queue_ptr, 0, get_rq_size(0));
+
+ ret = io_uring_register_ifq(&ring, &reg.zcrx);
+ if (ret) {
+ fprintf(stderr, "Can't register zcrx %i\n", ret);
+ return ret;
+ }
+
+ ret = t_create_socket_pair(fds, true);
+ if (ret) {
+ fprintf(stderr, "t_create_socket_pair failed: %d\n", ret);
+ return ret;
}
- ret = io_uring_register_ifq(&ring, &reg);
- if (ret != 0) {
- fprintf(stderr, "register_ifq failed: %d\n", ret);
- goto err_ring_exit;
+ if (pin_zcrx) {
+ struct zcrx_ctrl export_ctrl = {
+ .zcrx_id = reg.zcrx.zcrx_id,
+ .op = ZCRX_CTRL_EXPORT,
+ };
+
+ ret = t_zcrx_ctrl(&ring, &export_ctrl);
+ box_fd = export_ctrl.zc_export.zcrx_fd;
+ if (ret < 0) {
+ fprintf(stderr, "Export failed %i %i\n", ret, box_fd);
+ return ret;
+ }
}
- /*
- rd->ring_ptr = mmap(
- 0,
- reg.offsets.mmap_sz,
- PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_POPULATE,
- ring.enter_ring_fd,
- IORING_OFF_RQ_RING
- );
+ if (!iowq) {
+ sqe = io_uring_get_sqe(&ring);
+ test_io_uring_prep_zcrx(sqe, fds[0], reg.zcrx.zcrx_id);
+ ret = io_uring_submit(&ring);
+ if (ret != 1)
+ t_error(1, ret, "zcrx submit fail\n");
- rd->ring_sz = reg.offsets.mmap_sz;
- */
- rd->rq_ring.khead = (__u32*)((char*)rd->ring_ptr + reg.offsets.head);
- rd->rq_ring.ktail = (__u32*)((char*)rd->ring_ptr + reg.offsets.tail);
- rd->rq_ring.rqes = (struct io_uring_zcrx_rqe*)((char*)rd->ring_ptr + reg.offsets.rqes);
- rd->rq_ring.rq_tail = 0;
- rd->rq_ring.ring_entries = reg.rq_entries;
+ /* try to queue a task_work for the rx request */
+ ret = send(fds[1], buf, sizeof(buf), 0);
+ if (ret <= 0)
+ t_error(1, ret, "Send failed\n");
+ /* unregister zcrx with inflight request */
+ } else {
+ ret = send(fds[1], buf, sizeof(buf), 0);
+ if (ret <= 0)
+ t_error(1, ret, "Send failed\n");
- ret = recv_prep(&ring, rd, &sock, reg.zcrx_id);
- if (ret) {
- fprintf(stderr, "recv_prep failed: %d\n", ret);
- goto err;
+ sqe = io_uring_get_sqe(&ring);
+ test_io_uring_prep_zcrx(sqe, fds[0], reg.zcrx.zcrx_id);
+ sqe->flags |= IOSQE_ASYNC;
+ ret = io_uring_submit(&ring);
+ if (ret != 1)
+ t_error(1, ret, "zcrx submit fail\n");
+ /* unregister zcrx while io-wq processes a request */
}
- ret = do_recv(&ring, rd, area_reg.rq_area_token);
- close(sock);
- close(rd->accept_fd);
-err_ring_exit:
io_uring_queue_exit(&ring);
-err:
- return (void *)(intptr_t)ret;
+ /* give it time to exit before shutting the socket */
+ usleep(300);
+ close(fds[0]);
+ close(fds[1]);
+ if (box_fd != -1)
+ close(box_fd);
+ return 0;
}
-static int do_send(struct recv_data *rd)
+static int flush_invalid(struct t_executor *ctx, struct io_uring_zcrx_rqe *rqes,
+ unsigned nr)
{
- struct sockaddr_in saddr;
- struct iovec iov = {
- .iov_base = str,
- .iov_len = sizeof(str),
- };
- struct io_uring ring;
- struct io_uring_cqe *cqe;
- struct io_uring_sqe *sqe;
- int i, sockfd, ret;
+ struct io_uring_zcrx_rq *rq = &ctx->rq;
+ unsigned rq_mask = rq->ring_entries - 1;
+ struct io_uring_zcrx_rqe *rqe;
+ int i, ret;
- ret = io_uring_queue_init(8, &ring, 0);
- if (ret) {
- fprintf(stderr, "queue init failed: %d\n", ret);
- return 1;
- }
+ for (i = 0; i < nr; i++) {
+ rqe = &rq->rqes[rq->rq_tail & rq_mask];
+ memcpy(rqe, &rqes[i], sizeof(*rqe));
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(PORT);
- inet_pton(AF_INET, HOST, &saddr.sin_addr);
+ io_uring_smp_store_release(rq->ktail, ++rq->rq_tail);
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0) {
- perror("socket");
- goto err2;
+ ret = flush_rq(&ctx->ring, ctx->reg.zcrx.zcrx_id);
+ if (ret)
+ return ret;
}
+ return 0;
+}
- pthread_barrier_wait(&rd->connect);
+static int test_invalid_rq_pointers(void)
+{
+ struct t_executor ctx;
+ struct io_uring_zcrx_rq *rq = &ctx.rq;
+ int ret;
- ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
- if (ret < 0) {
- perror("connect");
- goto err;
- }
+ if (!rq_ctrl_op_supported(ZCRX_CTRL_FLUSH_RQ))
+ return 0;
+ ret = prep_server(&ctx);
+ if (ret)
+ return ret;
+ *rq->ktail = 0;
+ *rq->khead = 1;
+ (void)flush_rq(&ctx.ring, ctx.reg.zcrx.zcrx_id);
+ clean_server(&ctx);
- pthread_barrier_wait(&rd->startup);
+ ret = prep_server(&ctx);
+ if (ret)
+ return ret;
+ *rq->ktail = 2 * rq->ring_entries;
+ (void)flush_rq(&ctx.ring, ctx.reg.zcrx.zcrx_id);
+ clean_server(&ctx);
+ return 0;
+}
- for (i = 0; i < LOOP_COUNT; i++) {
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_send(sqe, sockfd, iov.iov_base, iov.iov_len, 0);
- sqe->user_data = 1;
- }
+static int test_invalid_rqes(void)
+{
+ struct io_uring_zcrx_rqe *rqe, rqes[16];
+ struct t_executor ctx;
+ __u64 area_token;
+ int ret, i;
- ret = io_uring_submit(&ring);
- if (ret <= 0) {
- fprintf(stderr, "submit failed: %d\n", ret);
- goto err;
- }
+ if (!rq_ctrl_op_supported(ZCRX_CTRL_FLUSH_RQ))
+ return 0;
- pthread_barrier_wait(&rd->barrier);
+ ret = prep_server(&ctx);
+ if (ret)
+ return ret;
+ area_token = ctx.reg.area.rq_area_token;
- ret = io_uring_wait_cqe(&ring, &cqe);
- if (cqe->res == -EINVAL) {
- fprintf(stdout, "send not supported, skipping\n");
- goto err;
- }
- if (cqe->res != iov.iov_len) {
- fprintf(stderr, "failed cqe: %d\n", cqe->res);
- goto err;
+ for (i = 0; i < 16; i++) {
+ rqe = &rqes[i];
+ rqe->off = area_token;
+ rqe->len = 1;
}
+ ret = flush_invalid(&ctx, rqes, 16);
+ if (ret)
+ return ret;
+ clean_server(&ctx);
+
+ ret = prep_server(&ctx);
+ if (ret)
+ return ret;
+ area_token = ctx.reg.area.rq_area_token;
- pthread_barrier_wait(&rd->finish);
+ rqe = &rqes[0];
+ rqe->off = area_token + ctx.reg.area.len;
+ rqe->len = 1;
- close(sockfd);
- io_uring_queue_exit(&ring);
- return 0;
+ rqe = &rqes[1];
+ rqe->off = ((uint64_t)1 << IORING_ZCRX_AREA_SHIFT) - 1;
+ rqe->len = 1;
-err:
- close(sockfd);
-err2:
- io_uring_queue_exit(&ring);
- pthread_barrier_wait(&rd->finish);
- return 1;
+ rqe = &rqes[2];
+ rqe->off = area_token + ((__u64)1 << IORING_ZCRX_AREA_SHIFT);
+ rqe->len = 1;
+
+ rqe = &rqes[3];
+ rqe->off = area_token;
+ rqe->len = 1;
+ rqe->__pad = 1;
+
+ ret = flush_invalid(&ctx, rqes, 4);
+ if (ret)
+ return ret;
+ clean_server(&ctx);
+ return 0;
}
-static int test_recv(void *area)
+static int test_area_ro(void)
{
- pthread_t recv_thread;
- struct recv_data rd;
+ struct zcrx_reg reg;
+ void *area;
int ret;
- void *retval;
- memset(&rd, 0, sizeof(rd));
- pthread_barrier_init(&rd.connect, NULL, 2);
- pthread_barrier_init(&rd.startup, NULL, 2);
- pthread_barrier_init(&rd.barrier, NULL, 2);
- pthread_barrier_init(&rd.finish, NULL, 2);
- rd.area = area;
+ default_reg(&reg, 0);
- ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
- if (ret) {
- fprintf(stderr, "Thread create failed: %d\n", ret);
- return 1;
+ area = mmap(NULL, reg.area.len, PROT_READ,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
+ if (area == MAP_FAILED) {
+ perror("mmap");
+ return T_EXIT_FAIL;
}
- do_send(&rd);
- pthread_join(recv_thread, &retval);
- return (intptr_t)retval;
+ reg.area.addr = uring_ptr_to_u64(area);
+ ret = try_register_zcrx(&reg.zcrx);
+ if (ret != -EFAULT) {
+ fprintf(stderr, "registered read-only memory\n");
+ return T_EXIT_FAIL;
+ }
+ munmap(area, AREA_SZ);
+ return 0;
}
-int main(int argc, char *argv[])
+static int run_tests(void)
{
- char *dev, *rxq_str, *rxq_end;
- void *area_outer, *area;
int ret;
+ int i;
- if (argc > 1)
+ ret = test_register_basic();
+ if (ret == -EPERM) {
+ printf("-EPERM, zcrx requires NET_ADMIN, skip\n");
return T_EXIT_SKIP;
+ }
+ if (ret) {
+ fprintf(stderr, "test_register_basic() failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
- page_size = sysconf(_SC_PAGESIZE);
- if (page_size < 0) {
- perror("sysconf(_SC_PAGESIZE)");
+ ret = test_rq();
+ if (ret) {
+ fprintf(stderr, "test_rq() failed %i\n", ret);
return T_EXIT_FAIL;
}
- area_outer = mmap(NULL, AREA_SZ + 8192, PROT_NONE,
- MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
- if (area_outer == MAP_FAILED) {
- perror("mmap");
+ ret = test_area();
+ if (ret) {
+ fprintf(stderr, "test_area() failed %i\n", ret);
return T_EXIT_FAIL;
}
- area = mmap(area_outer, AREA_SZ, PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
- if (area == MAP_FAILED) {
- perror("mmap");
+ ret = test_area_ro();
+ if (ret) {
+ fprintf(stderr, "test_area() failed %i\n", ret);
return T_EXIT_FAIL;
}
- dev = getenv(DEV_ENV_VAR);
- if (!dev)
- return T_EXIT_SKIP;
+ if (query.features & ZCRX_FEATURE_RX_PAGE_SIZE) {
+ ret = test_invalid_rx_page();
+ if (ret) {
+ fprintf(stderr, "test_invalid_rx_page() failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+ }
- ifidx = if_nametoindex(dev);
- if (!ifidx)
- return T_EXIT_SKIP;
+ ret = test_ro_params();
+ if (ret) {
+ fprintf(stderr, "test_ro_params() failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
- rxq_str = getenv(RXQ_ENV_VAR);
- if (!rxq_str)
- return T_EXIT_SKIP;
+ ret = test_invalid_recv();
+ if (ret) {
+ fprintf(stderr, "test_invalid_recv() failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
- rxq = strtol(rxq_str, &rxq_end, 10);
- if (rxq_end == rxq_str || *rxq_end != '\0')
- return T_EXIT_SKIP;
+ ret = test_exit_with_inflight();
+ if (ret) {
+ fprintf(stderr, "test_exit_with_inflight() failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
- ret = probe_zcrx(area);
- if (ret != T_EXIT_PASS)
- return ret;
+ if (query.register_flags & ZCRX_REG_IMPORT) {
+ ret = test_zcrx_invalid_clone();
+ if (ret) {
+ fprintf(stderr, "test_zcrx_invalid_clone() failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
- ret = test_rq_setup(area);
- if (ret) {
- fprintf(stderr, "test_invalid_reg_struct failed\n");
- return ret;
+ ret = test_zcrx_clone();
+ if (ret) {
+ fprintf(stderr, "test_zcrx_clone() failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+ } else {
+ printf("zcrx import is not supported, skip\n");
}
- ret = test_null_area_reg_struct();
+ ret = test_rq_flush();
if (ret) {
- fprintf(stderr, "test_null_area_reg_struct failed\n");
- return ret;
+ fprintf(stderr, "test_rq_flush() failed %i\n", ret);
+ return T_EXIT_FAIL;
}
- ret = test_null_area();
+ ret = test_invalid_rqes();
if (ret) {
- fprintf(stderr, "test_null_area failed\n");
- return ret;
+ fprintf(stderr, "test_invalid_rqes() failed %i\n", ret);
+ return T_EXIT_FAIL;
}
- ret = test_misaligned_area(area);
+ ret = test_invalid_rq_pointers();
if (ret) {
- fprintf(stderr, "test_misaligned_area failed\n");
- return ret;
+ fprintf(stderr, "test_invalid_rq_pointers() failed %i\n", ret);
+ return T_EXIT_FAIL;
}
- ret = test_larger_than_alloc_area(area);
+ ret = test_recv();
if (ret) {
- fprintf(stderr, "test_larger_than_alloc_area failed\n");
- return ret;
+ fprintf(stderr, "test_recv() failed %i\n", ret);
+ return T_EXIT_FAIL;
}
- ret = test_area_access();
- if (ret) {
- fprintf(stderr, "test_area_access failed\n");
- return ret;
+ for (i = 0; i < 4; i++) {
+ bool iowq = i & 1;
+ bool pin_zcrx = i & 2;
+
+ if (pin_zcrx && !(query.register_flags & ZCRX_REG_IMPORT))
+ continue;
+ ret = test_abnormal_exit(iowq, pin_zcrx);
+ if (ret) {
+ fprintf(stderr, "test_abnormal_exit(%i, %i) %i\n", iowq, pin_zcrx, ret);
+ return T_EXIT_FAIL;
+ }
}
- ret = test_invalid_if(area);
- if (ret) {
- fprintf(stderr, "test_invalid_if failed\n");
- return ret;
+ return T_EXIT_PASS;
+}
+
+static void setup(void)
+{
+ void *area_outer;
+
+ area_outer = mmap(NULL, AREA_SZ + 2 * page_size, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
+ if (area_outer == MAP_FAILED)
+ perror("mmap");
+
+ def_area_mem = mmap(area_outer + page_size, AREA_SZ, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
+ if (def_area_mem == MAP_FAILED)
+ perror("mmap");
+
+ def_hugepage_area_mem = mmap(NULL, HUGEPAGE_AREA_SZ, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_HUGE_2MB,
+ -1, 0);
+ if (def_hugepage_area_mem == MAP_FAILED) {
+ printf("can't allocate huge page, skip huge page tests\n");
+ def_hugepage_area_mem = NULL;
}
- ret = test_invalid_ifq_collision(area);
- if (ret) {
- fprintf(stderr, "test_invalid_ifq_collision failed\n");
- return ret;
+ def_rq_mem = mmap(NULL, get_rq_size(RQ_ENTRIES), PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ if (def_rq_mem == MAP_FAILED)
+ t_error(1, 0, "mmap(): refill ring");
+
+ def_area_mem = mmap(NULL, AREA_SZ, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ if (def_area_mem == MAP_FAILED)
+ t_error(1, 0, "mmap(): refill ring");
+ madvise(def_area_mem, AREA_SZ, MADV_NOHUGEPAGE);
+
+ ro_param_mem_size = T_ALIGN_UP(4096 * 2, page_size);
+ ro_param_mem = mmap(NULL, ro_param_mem_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (ro_param_mem == MAP_FAILED) {
+ fprintf(stderr, "null ro\n");
+ t_error(0, 1, "read-only mmap setup failed");
}
+}
- ret = test_invalid_zcrx_request(area);
- if (ret) {
- fprintf(stderr, "test_invalid_ifq_collision failed\n");
- return ret;
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ page_size = sysconf(_SC_PAGESIZE);
+ if (page_size < 0) {
+ perror("sysconf(_SC_PAGESIZE)");
+ return T_EXIT_FAIL;
}
- ret = test_recv(area);
- if (ret) {
- fprintf(stderr, "test_recv failed\n");
- return ret;
+ query_zcrx();
+
+ if (!zcrx_supported) {
+ printf("zcrx and query are not supported, skip");
+ return T_EXIT_SKIP;
+ }
+ if (!(query.register_flags & ZCRX_REG_NODEV)) {
+ printf("zcrx nodev mode not supported, skip");
+ return T_EXIT_SKIP;
}
- munmap(area, AREA_SZ);
- return T_EXIT_PASS;
+ setup();
+ return run_tests();
}
diff --git a/contrib/libs/liburing/test/zcrx.t/ya.make b/contrib/libs/liburing/test/zcrx.t/ya.make
index fdd3fb86874..56176b8d547 100644
--- a/contrib/libs/liburing/test/zcrx.t/ya.make
+++ b/contrib/libs/liburing/test/zcrx.t/ya.make
@@ -4,7 +4,7 @@ PROGRAM()
WITHOUT_LICENSE_TEXTS()
-VERSION(2.14)
+VERSION(2.15)
LICENSE(MIT)
diff --git a/contrib/libs/liburing/ya.make b/contrib/libs/liburing/ya.make
index 89da9682a2e..d5c1d053e17 100644
--- a/contrib/libs/liburing/ya.make
+++ b/contrib/libs/liburing/ya.make
@@ -2,9 +2,9 @@
LIBRARY()
-VERSION(2.14)
+VERSION(2.15)
-ORIGINAL_SOURCE(https://github.com/axboe/liburing/archive/liburing-2.14.tar.gz)
+ORIGINAL_SOURCE(https://github.com/axboe/liburing/archive/liburing-2.15.tar.gz)
LICENSE(
"(GPL-2.0-only WITH Linux-syscall-note OR MIT)" AND
@@ -47,6 +47,7 @@ RECURSE(
test/a0908ae19763.t
test/a4c0b3decb33.t
test/accept-link.t
+ test/accept-mshot-stress.t
test/accept-non-empty.t
test/accept-reuse.t
test/accept-test.t
@@ -55,9 +56,15 @@ RECURSE(
test/b19062a56726.t
test/b5837bd5311d.t
test/bind-listen.t
+ test/buf-ring-mshot.t
test/buf-ring-nommap.t
test/buf-ring-put.t
+ test/buf-ring-stress.t
+ test/buf-ring-upgrade.t
test/buf-ring.t
+ test/cancel-fd-userdata.t
+ test/cancel-race.t
+ test/cbpf_filter.t
test/ce593a6c480a.t
test/close-opath.t
test/cmd-discard.t
@@ -67,6 +74,7 @@ RECURSE(
test/coredump.t
test/cq-full.t
test/cq-overflow.t
+ test/cq-peek-batch-mixed.t
test/cq-peek-batch.t
test/cq-ready.t
test/cq-size.t
@@ -98,6 +106,7 @@ RECURSE(
test/fdinfo.t
test/fifo-futex-poll.t
test/fifo-nonblock-read.t
+ test/file-alloc-range-hint.t
test/file-exit-unreg.t
test/file-register.t
test/file-update.t
@@ -127,9 +136,11 @@ RECURSE(
test/io_uring_setup.t
test/iopoll-leak.t
test/iopoll-overflow.t
+ test/iopoll-sync.t
test/iopoll.t
test/iowait.t
test/kallsyms.t
+ test/large-resize.t
test/lfs-openat-write.t
test/lfs-openat.t
test/link-timeout.t
@@ -145,11 +156,14 @@ RECURSE(
test/msg-ring-flags.t
test/msg-ring-overflow.t
test/msg-ring.t
+ test/mshot-shutdown-race.t
test/multicqes_drain.t
test/napi-test.t
test/no-mmap-inval.t
test/nolibc.t
test/nop-all-sizes.t
+ test/nop-fixed-file-leak.t
+ test/nop-flags.t
test/nop.t
test/nop32-overflow.t
test/nop32.t
@@ -160,6 +174,7 @@ RECURSE(
test/openat2.t
test/personality.t
test/pipe-bug.t
+ test/pipe-direct-fixed.t
test/pipe-eof.t
test/pipe-reuse.t
test/pipe.t
@@ -170,6 +185,7 @@ RECURSE(
test/poll-many.t
test/poll-mshot-overflow.t
test/poll-mshot-update.t
+ test/poll-mshot-wake.t
test/poll-race-mshot.t
test/poll-race.t
test/poll-ring.t
@@ -179,17 +195,21 @@ RECURSE(
test/pollfree.t
test/probe.t
test/read-before-exit.t
+ test/read-inc-buf-more.t
test/read-inc-file.t
test/read-mshot-empty.t
test/read-mshot-stdin.t
test/read-mshot.t
test/read-write.t
+ test/recv-bundle-buf-len.t
test/recv-bundle-short-ooo.t
test/recv-inc-ooo.t
test/recv-msgall-stream.t
test/recv-msgall.t
+ test/recv-mshot-drain.t
test/recv-mshot-fair.t
test/recv-multishot.t
+ test/recvmsg-inc-tail.t
test/recvsend_bundle-inc.t
test/recvsend_bundle.t
test/reg-fd-only.t
@@ -200,6 +220,7 @@ RECURSE(
test/regbuf-merge.t
test/register-restrictions.t
test/rename.t
+ test/resize-mmap-fail.t
test/resize-rings.t
test/ring-leak.t
test/ring-leak2.t
@@ -236,6 +257,7 @@ RECURSE(
test/sq-poll-share.t
test/sq-space_left.t
test/sqe-mixed-bad-wrap.t
+ test/sqe-mixed-boundary.t
test/sqe-mixed-nop.t
test/sqe-mixed-uring_cmd.t
test/sqpoll-disable-exit.t
@@ -249,8 +271,10 @@ RECURSE(
test/submit-reuse.t
test/symlink.t
test/sync-cancel.t
+ test/task-restrict.t
test/teardowns.t
test/thread-exit.t
+ test/timens-abs-timer.t
test/timeout-new.t
test/timeout.t
test/timerfd-short-read.t
diff --git a/contrib/libs/openssl/.yandex_meta/__init__.py b/contrib/libs/openssl/.yandex_meta/__init__.py
index 03d0dc5a5da..4f6dd93ab45 100644
--- a/contrib/libs/openssl/.yandex_meta/__init__.py
+++ b/contrib/libs/openssl/.yandex_meta/__init__.py
@@ -5,6 +5,11 @@ from devtools.yamaker import pathutil
from devtools.yamaker.modules import Linkable, Switch
from devtools.yamaker.project import NixProject
+QUICTLS_SRCS = [
+ "ssl/ssl_quic.c",
+ "ssl/statem_quic.c",
+]
+
def post_build(self):
os.remove(f"{self.dstdir}/libssl.map")
@@ -20,6 +25,9 @@ def post_install(self):
def d(s):
return self.dstdir + "/" + s
+ with self.yamakes["."] as openssl:
+ openssl.SRCS |= set(QUICTLS_SRCS)
+
# Move asm sources to asm/linux.
fileutil.subcopy(self.dstdir, d("asm/linux"), ["**/*.s"], move=True)
with self.yamakes["crypto"] as m:
@@ -141,12 +149,12 @@ openssl = NixProject(
"include/**/*.h",
],
platform_dispatchers=[
- "apps/progs.h",
"crypto/buildinf.h",
"include/crypto/bn_conf.h",
"include/crypto/dso_conf.h",
"include/openssl/opensslconf.h",
],
+ # fmt: off
keep_paths=[
# This asm files were generated manually
"asm/aarch64/",
@@ -162,7 +170,8 @@ openssl = NixProject(
"crypto/ubsan.supp",
"redef.h",
"system_openssl.ya.inc",
- ],
+ ] + QUICTLS_SRCS,
+ # fmt: on
post_build=post_build,
post_install=post_install,
)
diff --git a/contrib/libs/openssl/apps/progs-linux.h b/contrib/libs/openssl/apps/progs-linux.h
deleted file mode 100644
index b7c2e31b284..00000000000
--- a/contrib/libs/openssl/apps/progs-linux.h
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * WARNING: do not edit!
- * Generated by apps/progs.pl
- *
- * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the OpenSSL license (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-typedef enum FUNC_TYPE {
- FT_none, FT_general, FT_md, FT_cipher, FT_pkey,
- FT_md_alg, FT_cipher_alg
-} FUNC_TYPE;
-
-typedef struct function_st {
- FUNC_TYPE type;
- const char *name;
- int (*func)(int argc, char *argv[]);
- const OPTIONS *help;
-} FUNCTION;
-
-DEFINE_LHASH_OF(FUNCTION);
-
-extern int asn1parse_main(int argc, char *argv[]);
-extern int ca_main(int argc, char *argv[]);
-extern int ciphers_main(int argc, char *argv[]);
-extern int cms_main(int argc, char *argv[]);
-extern int crl_main(int argc, char *argv[]);
-extern int crl2pkcs7_main(int argc, char *argv[]);
-extern int dgst_main(int argc, char *argv[]);
-extern int dhparam_main(int argc, char *argv[]);
-extern int dsa_main(int argc, char *argv[]);
-extern int dsaparam_main(int argc, char *argv[]);
-extern int ec_main(int argc, char *argv[]);
-extern int ecparam_main(int argc, char *argv[]);
-extern int enc_main(int argc, char *argv[]);
-extern int engine_main(int argc, char *argv[]);
-extern int errstr_main(int argc, char *argv[]);
-extern int gendsa_main(int argc, char *argv[]);
-extern int genpkey_main(int argc, char *argv[]);
-extern int genrsa_main(int argc, char *argv[]);
-extern int help_main(int argc, char *argv[]);
-extern int list_main(int argc, char *argv[]);
-extern int nseq_main(int argc, char *argv[]);
-extern int ocsp_main(int argc, char *argv[]);
-extern int passwd_main(int argc, char *argv[]);
-extern int pkcs12_main(int argc, char *argv[]);
-extern int pkcs7_main(int argc, char *argv[]);
-extern int pkcs8_main(int argc, char *argv[]);
-extern int pkey_main(int argc, char *argv[]);
-extern int pkeyparam_main(int argc, char *argv[]);
-extern int pkeyutl_main(int argc, char *argv[]);
-extern int prime_main(int argc, char *argv[]);
-extern int rand_main(int argc, char *argv[]);
-extern int rehash_main(int argc, char *argv[]);
-extern int req_main(int argc, char *argv[]);
-extern int rsa_main(int argc, char *argv[]);
-extern int rsautl_main(int argc, char *argv[]);
-extern int s_client_main(int argc, char *argv[]);
-extern int s_server_main(int argc, char *argv[]);
-extern int s_time_main(int argc, char *argv[]);
-extern int sess_id_main(int argc, char *argv[]);
-extern int smime_main(int argc, char *argv[]);
-extern int speed_main(int argc, char *argv[]);
-extern int spkac_main(int argc, char *argv[]);
-extern int srp_main(int argc, char *argv[]);
-extern int storeutl_main(int argc, char *argv[]);
-extern int ts_main(int argc, char *argv[]);
-extern int verify_main(int argc, char *argv[]);
-extern int version_main(int argc, char *argv[]);
-extern int x509_main(int argc, char *argv[]);
-
-extern const OPTIONS asn1parse_options[];
-extern const OPTIONS ca_options[];
-extern const OPTIONS ciphers_options[];
-extern const OPTIONS cms_options[];
-extern const OPTIONS crl_options[];
-extern const OPTIONS crl2pkcs7_options[];
-extern const OPTIONS dgst_options[];
-extern const OPTIONS dhparam_options[];
-extern const OPTIONS dsa_options[];
-extern const OPTIONS dsaparam_options[];
-extern const OPTIONS ec_options[];
-extern const OPTIONS ecparam_options[];
-extern const OPTIONS enc_options[];
-extern const OPTIONS engine_options[];
-extern const OPTIONS errstr_options[];
-extern const OPTIONS gendsa_options[];
-extern const OPTIONS genpkey_options[];
-extern const OPTIONS genrsa_options[];
-extern const OPTIONS help_options[];
-extern const OPTIONS list_options[];
-extern const OPTIONS nseq_options[];
-extern const OPTIONS ocsp_options[];
-extern const OPTIONS passwd_options[];
-extern const OPTIONS pkcs12_options[];
-extern const OPTIONS pkcs7_options[];
-extern const OPTIONS pkcs8_options[];
-extern const OPTIONS pkey_options[];
-extern const OPTIONS pkeyparam_options[];
-extern const OPTIONS pkeyutl_options[];
-extern const OPTIONS prime_options[];
-extern const OPTIONS rand_options[];
-extern const OPTIONS rehash_options[];
-extern const OPTIONS req_options[];
-extern const OPTIONS rsa_options[];
-extern const OPTIONS rsautl_options[];
-extern const OPTIONS s_client_options[];
-extern const OPTIONS s_server_options[];
-extern const OPTIONS s_time_options[];
-extern const OPTIONS sess_id_options[];
-extern const OPTIONS smime_options[];
-extern const OPTIONS speed_options[];
-extern const OPTIONS spkac_options[];
-extern const OPTIONS srp_options[];
-extern const OPTIONS storeutl_options[];
-extern const OPTIONS ts_options[];
-extern const OPTIONS verify_options[];
-extern const OPTIONS version_options[];
-extern const OPTIONS x509_options[];
-
-#ifdef INCLUDE_FUNCTION_TABLE
-static FUNCTION functions[] = {
- {FT_general, "asn1parse", asn1parse_main, asn1parse_options},
- {FT_general, "ca", ca_main, ca_options},
-#ifndef OPENSSL_NO_SOCK
- {FT_general, "ciphers", ciphers_main, ciphers_options},
-#endif
-#ifndef OPENSSL_NO_CMS
- {FT_general, "cms", cms_main, cms_options},
-#endif
- {FT_general, "crl", crl_main, crl_options},
- {FT_general, "crl2pkcs7", crl2pkcs7_main, crl2pkcs7_options},
- {FT_general, "dgst", dgst_main, dgst_options},
-#ifndef OPENSSL_NO_DH
- {FT_general, "dhparam", dhparam_main, dhparam_options},
-#endif
-#ifndef OPENSSL_NO_DSA
- {FT_general, "dsa", dsa_main, dsa_options},
-#endif
-#ifndef OPENSSL_NO_DSA
- {FT_general, "dsaparam", dsaparam_main, dsaparam_options},
-#endif
-#ifndef OPENSSL_NO_EC
- {FT_general, "ec", ec_main, ec_options},
-#endif
-#ifndef OPENSSL_NO_EC
- {FT_general, "ecparam", ecparam_main, ecparam_options},
-#endif
- {FT_general, "enc", enc_main, enc_options},
-#ifndef OPENSSL_NO_ENGINE
- {FT_general, "engine", engine_main, engine_options},
-#endif
- {FT_general, "errstr", errstr_main, errstr_options},
-#ifndef OPENSSL_NO_DSA
- {FT_general, "gendsa", gendsa_main, gendsa_options},
-#endif
- {FT_general, "genpkey", genpkey_main, genpkey_options},
-#ifndef OPENSSL_NO_RSA
- {FT_general, "genrsa", genrsa_main, genrsa_options},
-#endif
- {FT_general, "help", help_main, help_options},
- {FT_general, "list", list_main, list_options},
- {FT_general, "nseq", nseq_main, nseq_options},
-#ifndef OPENSSL_NO_OCSP
- {FT_general, "ocsp", ocsp_main, ocsp_options},
-#endif
- {FT_general, "passwd", passwd_main, passwd_options},
-#ifndef OPENSSL_NO_DES
- {FT_general, "pkcs12", pkcs12_main, pkcs12_options},
-#endif
- {FT_general, "pkcs7", pkcs7_main, pkcs7_options},
- {FT_general, "pkcs8", pkcs8_main, pkcs8_options},
- {FT_general, "pkey", pkey_main, pkey_options},
- {FT_general, "pkeyparam", pkeyparam_main, pkeyparam_options},
- {FT_general, "pkeyutl", pkeyutl_main, pkeyutl_options},
- {FT_general, "prime", prime_main, prime_options},
- {FT_general, "rand", rand_main, rand_options},
- {FT_general, "rehash", rehash_main, rehash_options},
- {FT_general, "req", req_main, req_options},
- {FT_general, "rsa", rsa_main, rsa_options},
-#ifndef OPENSSL_NO_RSA
- {FT_general, "rsautl", rsautl_main, rsautl_options},
-#endif
-#ifndef OPENSSL_NO_SOCK
- {FT_general, "s_client", s_client_main, s_client_options},
-#endif
-#ifndef OPENSSL_NO_SOCK
- {FT_general, "s_server", s_server_main, s_server_options},
-#endif
-#ifndef OPENSSL_NO_SOCK
- {FT_general, "s_time", s_time_main, s_time_options},
-#endif
- {FT_general, "sess_id", sess_id_main, sess_id_options},
- {FT_general, "smime", smime_main, smime_options},
- {FT_general, "speed", speed_main, speed_options},
- {FT_general, "spkac", spkac_main, spkac_options},
-#ifndef OPENSSL_NO_SRP
- {FT_general, "srp", srp_main, srp_options},
-#endif
- {FT_general, "storeutl", storeutl_main, storeutl_options},
-#ifndef OPENSSL_NO_TS
- {FT_general, "ts", ts_main, ts_options},
-#endif
- {FT_general, "verify", verify_main, verify_options},
- {FT_general, "version", version_main, version_options},
- {FT_general, "x509", x509_main, x509_options},
-#ifndef OPENSSL_NO_MD2
- {FT_md, "md2", dgst_main},
-#endif
-#ifndef OPENSSL_NO_MD4
- {FT_md, "md4", dgst_main},
-#endif
- {FT_md, "md5", dgst_main},
-#ifndef OPENSSL_NO_GOST
- {FT_md, "gost", dgst_main},
-#endif
- {FT_md, "sha1", dgst_main},
- {FT_md, "sha224", dgst_main},
- {FT_md, "sha256", dgst_main},
- {FT_md, "sha384", dgst_main},
- {FT_md, "sha512", dgst_main},
- {FT_md, "sha512-224", dgst_main},
- {FT_md, "sha512-256", dgst_main},
- {FT_md, "sha3-224", dgst_main},
- {FT_md, "sha3-256", dgst_main},
- {FT_md, "sha3-384", dgst_main},
- {FT_md, "sha3-512", dgst_main},
- {FT_md, "shake128", dgst_main},
- {FT_md, "shake256", dgst_main},
-#ifndef OPENSSL_NO_MDC2
- {FT_md, "mdc2", dgst_main},
-#endif
-#ifndef OPENSSL_NO_RMD160
- {FT_md, "rmd160", dgst_main},
-#endif
-#ifndef OPENSSL_NO_BLAKE2
- {FT_md, "blake2b512", dgst_main},
-#endif
-#ifndef OPENSSL_NO_BLAKE2
- {FT_md, "blake2s256", dgst_main},
-#endif
-#ifndef OPENSSL_NO_SM3
- {FT_md, "sm3", dgst_main},
-#endif
- {FT_cipher, "aes-128-cbc", enc_main, enc_options},
- {FT_cipher, "aes-128-ecb", enc_main, enc_options},
- {FT_cipher, "aes-192-cbc", enc_main, enc_options},
- {FT_cipher, "aes-192-ecb", enc_main, enc_options},
- {FT_cipher, "aes-256-cbc", enc_main, enc_options},
- {FT_cipher, "aes-256-ecb", enc_main, enc_options},
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-ctr", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-cfb1", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-cfb8", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-ctr", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-cfb1", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-cfb8", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-ctr", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-cfb1", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-cfb8", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-128-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-128-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-192-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-192-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-256-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-256-ecb", enc_main, enc_options},
-#endif
- {FT_cipher, "base64", enc_main, enc_options},
-#ifdef ZLIB
- {FT_cipher, "zlib", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des3", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "desx", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC4
- {FT_cipher, "rc4", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC4
- {FT_cipher, "rc4-40", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede3", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede3-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede3-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede3-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-64-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-40-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast5-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast5-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast5-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast5-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-ctr", enc_main, enc_options},
-#endif
- {0, NULL, NULL}
-};
-#endif
diff --git a/contrib/libs/openssl/apps/progs-win.h b/contrib/libs/openssl/apps/progs-win.h
deleted file mode 100644
index 9b3d270e208..00000000000
--- a/contrib/libs/openssl/apps/progs-win.h
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * WARNING: do not edit!
- * Generated by apps/progs.pl
- *
- * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the OpenSSL license (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-typedef enum FUNC_TYPE {
- FT_none, FT_general, FT_md, FT_cipher, FT_pkey,
- FT_md_alg, FT_cipher_alg
-} FUNC_TYPE;
-
-typedef struct function_st {
- FUNC_TYPE type;
- const char *name;
- int (*func)(int argc, char *argv[]);
- const OPTIONS *help;
-} FUNCTION;
-
-DEFINE_LHASH_OF(FUNCTION);
-
-extern int asn1parse_main(int argc, char *argv[]);
-extern int ca_main(int argc, char *argv[]);
-extern int ciphers_main(int argc, char *argv[]);
-extern int cms_main(int argc, char *argv[]);
-extern int crl_main(int argc, char *argv[]);
-extern int crl2pkcs7_main(int argc, char *argv[]);
-extern int dgst_main(int argc, char *argv[]);
-extern int dhparam_main(int argc, char *argv[]);
-extern int dsa_main(int argc, char *argv[]);
-extern int dsaparam_main(int argc, char *argv[]);
-extern int ec_main(int argc, char *argv[]);
-extern int ecparam_main(int argc, char *argv[]);
-extern int enc_main(int argc, char *argv[]);
-extern int engine_main(int argc, char *argv[]);
-extern int errstr_main(int argc, char *argv[]);
-extern int gendsa_main(int argc, char *argv[]);
-extern int genpkey_main(int argc, char *argv[]);
-extern int genrsa_main(int argc, char *argv[]);
-extern int help_main(int argc, char *argv[]);
-extern int list_main(int argc, char *argv[]);
-extern int nseq_main(int argc, char *argv[]);
-extern int ocsp_main(int argc, char *argv[]);
-extern int passwd_main(int argc, char *argv[]);
-extern int pkcs12_main(int argc, char *argv[]);
-extern int pkcs7_main(int argc, char *argv[]);
-extern int pkcs8_main(int argc, char *argv[]);
-extern int pkey_main(int argc, char *argv[]);
-extern int pkeyparam_main(int argc, char *argv[]);
-extern int pkeyutl_main(int argc, char *argv[]);
-extern int prime_main(int argc, char *argv[]);
-extern int rand_main(int argc, char *argv[]);
-extern int rehash_main(int argc, char *argv[]);
-extern int req_main(int argc, char *argv[]);
-extern int rsa_main(int argc, char *argv[]);
-extern int rsautl_main(int argc, char *argv[]);
-extern int s_client_main(int argc, char *argv[]);
-extern int s_server_main(int argc, char *argv[]);
-extern int s_time_main(int argc, char *argv[]);
-extern int sess_id_main(int argc, char *argv[]);
-extern int smime_main(int argc, char *argv[]);
-extern int speed_main(int argc, char *argv[]);
-extern int spkac_main(int argc, char *argv[]);
-extern int srp_main(int argc, char *argv[]);
-extern int storeutl_main(int argc, char *argv[]);
-extern int ts_main(int argc, char *argv[]);
-extern int verify_main(int argc, char *argv[]);
-extern int version_main(int argc, char *argv[]);
-extern int x509_main(int argc, char *argv[]);
-
-extern const OPTIONS asn1parse_options[];
-extern const OPTIONS ca_options[];
-extern const OPTIONS ciphers_options[];
-extern const OPTIONS cms_options[];
-extern const OPTIONS crl_options[];
-extern const OPTIONS crl2pkcs7_options[];
-extern const OPTIONS dgst_options[];
-extern const OPTIONS dhparam_options[];
-extern const OPTIONS dsa_options[];
-extern const OPTIONS dsaparam_options[];
-extern const OPTIONS ec_options[];
-extern const OPTIONS ecparam_options[];
-extern const OPTIONS enc_options[];
-extern const OPTIONS engine_options[];
-extern const OPTIONS errstr_options[];
-extern const OPTIONS gendsa_options[];
-extern const OPTIONS genpkey_options[];
-extern const OPTIONS genrsa_options[];
-extern const OPTIONS help_options[];
-extern const OPTIONS list_options[];
-extern const OPTIONS nseq_options[];
-extern const OPTIONS ocsp_options[];
-extern const OPTIONS passwd_options[];
-extern const OPTIONS pkcs12_options[];
-extern const OPTIONS pkcs7_options[];
-extern const OPTIONS pkcs8_options[];
-extern const OPTIONS pkey_options[];
-extern const OPTIONS pkeyparam_options[];
-extern const OPTIONS pkeyutl_options[];
-extern const OPTIONS prime_options[];
-extern const OPTIONS rand_options[];
-extern const OPTIONS rehash_options[];
-extern const OPTIONS req_options[];
-extern const OPTIONS rsa_options[];
-extern const OPTIONS rsautl_options[];
-extern const OPTIONS s_client_options[];
-extern const OPTIONS s_server_options[];
-extern const OPTIONS s_time_options[];
-extern const OPTIONS sess_id_options[];
-extern const OPTIONS smime_options[];
-extern const OPTIONS speed_options[];
-extern const OPTIONS spkac_options[];
-extern const OPTIONS srp_options[];
-extern const OPTIONS storeutl_options[];
-extern const OPTIONS ts_options[];
-extern const OPTIONS verify_options[];
-extern const OPTIONS version_options[];
-extern const OPTIONS x509_options[];
-
-#ifdef INCLUDE_FUNCTION_TABLE
-static FUNCTION functions[] = {
- {FT_general, "asn1parse", asn1parse_main, asn1parse_options},
- {FT_general, "ca", ca_main, ca_options},
-#ifndef OPENSSL_NO_SOCK
- {FT_general, "ciphers", ciphers_main, ciphers_options},
-#endif
-#ifndef OPENSSL_NO_CMS
- {FT_general, "cms", cms_main, cms_options},
-#endif
- {FT_general, "crl", crl_main, crl_options},
- {FT_general, "crl2pkcs7", crl2pkcs7_main, crl2pkcs7_options},
- {FT_general, "dgst", dgst_main, dgst_options},
-#ifndef OPENSSL_NO_DH
- {FT_general, "dhparam", dhparam_main, dhparam_options},
-#endif
-#ifndef OPENSSL_NO_DSA
- {FT_general, "dsa", dsa_main, dsa_options},
-#endif
-#ifndef OPENSSL_NO_DSA
- {FT_general, "dsaparam", dsaparam_main, dsaparam_options},
-#endif
-#ifndef OPENSSL_NO_EC
- {FT_general, "ec", ec_main, ec_options},
-#endif
-#ifndef OPENSSL_NO_EC
- {FT_general, "ecparam", ecparam_main, ecparam_options},
-#endif
- {FT_general, "enc", enc_main, enc_options},
-#ifndef OPENSSL_NO_ENGINE
- {FT_general, "engine", engine_main, engine_options},
-#endif
- {FT_general, "errstr", errstr_main, errstr_options},
-#ifndef OPENSSL_NO_DSA
- {FT_general, "gendsa", gendsa_main, gendsa_options},
-#endif
- {FT_general, "genpkey", genpkey_main, genpkey_options},
-#ifndef OPENSSL_NO_RSA
- {FT_general, "genrsa", genrsa_main, genrsa_options},
-#endif
- {FT_general, "help", help_main, help_options},
- {FT_general, "list", list_main, list_options},
- {FT_general, "nseq", nseq_main, nseq_options},
-#ifndef OPENSSL_NO_OCSP
- {FT_general, "ocsp", ocsp_main, ocsp_options},
-#endif
- {FT_general, "passwd", passwd_main, passwd_options},
-#ifndef OPENSSL_NO_DES
- {FT_general, "pkcs12", pkcs12_main, pkcs12_options},
-#endif
- {FT_general, "pkcs7", pkcs7_main, pkcs7_options},
- {FT_general, "pkcs8", pkcs8_main, pkcs8_options},
- {FT_general, "pkey", pkey_main, pkey_options},
- {FT_general, "pkeyparam", pkeyparam_main, pkeyparam_options},
- {FT_general, "pkeyutl", pkeyutl_main, pkeyutl_options},
- {FT_general, "prime", prime_main, prime_options},
- {FT_general, "rand", rand_main, rand_options},
- {FT_general, "rehash", rehash_main, rehash_options},
- {FT_general, "req", req_main, req_options},
- {FT_general, "rsa", rsa_main, rsa_options},
-#ifndef OPENSSL_NO_RSA
- {FT_general, "rsautl", rsautl_main, rsautl_options},
-#endif
-#ifndef OPENSSL_NO_SOCK
- {FT_general, "s_client", s_client_main, s_client_options},
-#endif
-#ifndef OPENSSL_NO_SOCK
- {FT_general, "s_server", s_server_main, s_server_options},
-#endif
-#ifndef OPENSSL_NO_SOCK
- {FT_general, "s_time", s_time_main, s_time_options},
-#endif
- {FT_general, "sess_id", sess_id_main, sess_id_options},
- {FT_general, "smime", smime_main, smime_options},
- {FT_general, "speed", speed_main, speed_options},
- {FT_general, "spkac", spkac_main, spkac_options},
-#ifndef OPENSSL_NO_SRP
- {FT_general, "srp", srp_main, srp_options},
-#endif
- {FT_general, "storeutl", storeutl_main, storeutl_options},
-#ifndef OPENSSL_NO_TS
- {FT_general, "ts", ts_main, ts_options},
-#endif
- {FT_general, "verify", verify_main, verify_options},
- {FT_general, "version", version_main, version_options},
- {FT_general, "x509", x509_main, x509_options},
-#ifndef OPENSSL_NO_MD2
- {FT_md, "md2", dgst_main},
-#endif
-#ifndef OPENSSL_NO_MD4
- {FT_md, "md4", dgst_main},
-#endif
- {FT_md, "md5", dgst_main},
-#ifndef OPENSSL_NO_GOST
- {FT_md, "gost", dgst_main},
-#endif
- {FT_md, "sha1", dgst_main},
- {FT_md, "sha224", dgst_main},
- {FT_md, "sha256", dgst_main},
- {FT_md, "sha384", dgst_main},
- {FT_md, "sha512", dgst_main},
- {FT_md, "sha512-224", dgst_main},
- {FT_md, "sha512-256", dgst_main},
- {FT_md, "sha3-224", dgst_main},
- {FT_md, "sha3-256", dgst_main},
- {FT_md, "sha3-384", dgst_main},
- {FT_md, "sha3-512", dgst_main},
- {FT_md, "shake128", dgst_main},
- {FT_md, "shake256", dgst_main},
-#ifndef OPENSSL_NO_MDC2
- {FT_md, "mdc2", dgst_main},
-#endif
-#ifndef OPENSSL_NO_RMD160
- {FT_md, "rmd160", dgst_main},
-#endif
-#ifndef OPENSSL_NO_BLAKE2
- {FT_md, "blake2b512", dgst_main},
-#endif
-#ifndef OPENSSL_NO_BLAKE2
- {FT_md, "blake2s256", dgst_main},
-#endif
-#ifndef OPENSSL_NO_SM3
- {FT_md, "sm3", dgst_main},
-#endif
- {FT_cipher, "aes-128-cbc", enc_main, enc_options},
- {FT_cipher, "aes-128-ecb", enc_main, enc_options},
- {FT_cipher, "aes-192-cbc", enc_main, enc_options},
- {FT_cipher, "aes-192-ecb", enc_main, enc_options},
- {FT_cipher, "aes-256-cbc", enc_main, enc_options},
- {FT_cipher, "aes-256-ecb", enc_main, enc_options},
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-ctr", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-cfb1", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-128-cfb8", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-ctr", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-cfb1", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-192-cfb8", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-ctr", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-cfb1", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_ARIA
- {FT_cipher, "aria-256-cfb8", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-128-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-128-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-192-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-192-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-256-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAMELLIA
- {FT_cipher, "camellia-256-ecb", enc_main, enc_options},
-#endif
- {FT_cipher, "base64", enc_main, enc_options},
-#ifdef ZLIB
- {FT_cipher, "zlib", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des3", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "desx", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC4
- {FT_cipher, "rc4", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC4
- {FT_cipher, "rc4-40", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede3", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede3-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede3-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_DES
- {FT_cipher, "des-ede3-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_IDEA
- {FT_cipher, "idea-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SEED
- {FT_cipher, "seed-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-64-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC2
- {FT_cipher, "rc2-40-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_BF
- {FT_cipher, "bf-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast5-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast5-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast5-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast5-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_CAST
- {FT_cipher, "cast-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_RC5
- {FT_cipher, "rc5-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-cbc", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-ecb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-cfb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-ofb", enc_main, enc_options},
-#endif
-#ifndef OPENSSL_NO_SM4
- {FT_cipher, "sm4-ctr", enc_main, enc_options},
-#endif
- {0, NULL, NULL}
-};
-#endif
diff --git a/contrib/libs/openssl/apps/progs.h b/contrib/libs/openssl/apps/progs.h
index 43d8fb530f6..b7c2e31b284 100644
--- a/contrib/libs/openssl/apps/progs.h
+++ b/contrib/libs/openssl/apps/progs.h
@@ -1,7 +1,507 @@
-#pragma once
+/*
+ * WARNING: do not edit!
+ * Generated by apps/progs.pl
+ *
+ * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
-#if defined(_MSC_VER)
-# include "progs-win.h"
-#else
-# include "progs-linux.h"
+typedef enum FUNC_TYPE {
+ FT_none, FT_general, FT_md, FT_cipher, FT_pkey,
+ FT_md_alg, FT_cipher_alg
+} FUNC_TYPE;
+
+typedef struct function_st {
+ FUNC_TYPE type;
+ const char *name;
+ int (*func)(int argc, char *argv[]);
+ const OPTIONS *help;
+} FUNCTION;
+
+DEFINE_LHASH_OF(FUNCTION);
+
+extern int asn1parse_main(int argc, char *argv[]);
+extern int ca_main(int argc, char *argv[]);
+extern int ciphers_main(int argc, char *argv[]);
+extern int cms_main(int argc, char *argv[]);
+extern int crl_main(int argc, char *argv[]);
+extern int crl2pkcs7_main(int argc, char *argv[]);
+extern int dgst_main(int argc, char *argv[]);
+extern int dhparam_main(int argc, char *argv[]);
+extern int dsa_main(int argc, char *argv[]);
+extern int dsaparam_main(int argc, char *argv[]);
+extern int ec_main(int argc, char *argv[]);
+extern int ecparam_main(int argc, char *argv[]);
+extern int enc_main(int argc, char *argv[]);
+extern int engine_main(int argc, char *argv[]);
+extern int errstr_main(int argc, char *argv[]);
+extern int gendsa_main(int argc, char *argv[]);
+extern int genpkey_main(int argc, char *argv[]);
+extern int genrsa_main(int argc, char *argv[]);
+extern int help_main(int argc, char *argv[]);
+extern int list_main(int argc, char *argv[]);
+extern int nseq_main(int argc, char *argv[]);
+extern int ocsp_main(int argc, char *argv[]);
+extern int passwd_main(int argc, char *argv[]);
+extern int pkcs12_main(int argc, char *argv[]);
+extern int pkcs7_main(int argc, char *argv[]);
+extern int pkcs8_main(int argc, char *argv[]);
+extern int pkey_main(int argc, char *argv[]);
+extern int pkeyparam_main(int argc, char *argv[]);
+extern int pkeyutl_main(int argc, char *argv[]);
+extern int prime_main(int argc, char *argv[]);
+extern int rand_main(int argc, char *argv[]);
+extern int rehash_main(int argc, char *argv[]);
+extern int req_main(int argc, char *argv[]);
+extern int rsa_main(int argc, char *argv[]);
+extern int rsautl_main(int argc, char *argv[]);
+extern int s_client_main(int argc, char *argv[]);
+extern int s_server_main(int argc, char *argv[]);
+extern int s_time_main(int argc, char *argv[]);
+extern int sess_id_main(int argc, char *argv[]);
+extern int smime_main(int argc, char *argv[]);
+extern int speed_main(int argc, char *argv[]);
+extern int spkac_main(int argc, char *argv[]);
+extern int srp_main(int argc, char *argv[]);
+extern int storeutl_main(int argc, char *argv[]);
+extern int ts_main(int argc, char *argv[]);
+extern int verify_main(int argc, char *argv[]);
+extern int version_main(int argc, char *argv[]);
+extern int x509_main(int argc, char *argv[]);
+
+extern const OPTIONS asn1parse_options[];
+extern const OPTIONS ca_options[];
+extern const OPTIONS ciphers_options[];
+extern const OPTIONS cms_options[];
+extern const OPTIONS crl_options[];
+extern const OPTIONS crl2pkcs7_options[];
+extern const OPTIONS dgst_options[];
+extern const OPTIONS dhparam_options[];
+extern const OPTIONS dsa_options[];
+extern const OPTIONS dsaparam_options[];
+extern const OPTIONS ec_options[];
+extern const OPTIONS ecparam_options[];
+extern const OPTIONS enc_options[];
+extern const OPTIONS engine_options[];
+extern const OPTIONS errstr_options[];
+extern const OPTIONS gendsa_options[];
+extern const OPTIONS genpkey_options[];
+extern const OPTIONS genrsa_options[];
+extern const OPTIONS help_options[];
+extern const OPTIONS list_options[];
+extern const OPTIONS nseq_options[];
+extern const OPTIONS ocsp_options[];
+extern const OPTIONS passwd_options[];
+extern const OPTIONS pkcs12_options[];
+extern const OPTIONS pkcs7_options[];
+extern const OPTIONS pkcs8_options[];
+extern const OPTIONS pkey_options[];
+extern const OPTIONS pkeyparam_options[];
+extern const OPTIONS pkeyutl_options[];
+extern const OPTIONS prime_options[];
+extern const OPTIONS rand_options[];
+extern const OPTIONS rehash_options[];
+extern const OPTIONS req_options[];
+extern const OPTIONS rsa_options[];
+extern const OPTIONS rsautl_options[];
+extern const OPTIONS s_client_options[];
+extern const OPTIONS s_server_options[];
+extern const OPTIONS s_time_options[];
+extern const OPTIONS sess_id_options[];
+extern const OPTIONS smime_options[];
+extern const OPTIONS speed_options[];
+extern const OPTIONS spkac_options[];
+extern const OPTIONS srp_options[];
+extern const OPTIONS storeutl_options[];
+extern const OPTIONS ts_options[];
+extern const OPTIONS verify_options[];
+extern const OPTIONS version_options[];
+extern const OPTIONS x509_options[];
+
+#ifdef INCLUDE_FUNCTION_TABLE
+static FUNCTION functions[] = {
+ {FT_general, "asn1parse", asn1parse_main, asn1parse_options},
+ {FT_general, "ca", ca_main, ca_options},
+#ifndef OPENSSL_NO_SOCK
+ {FT_general, "ciphers", ciphers_main, ciphers_options},
+#endif
+#ifndef OPENSSL_NO_CMS
+ {FT_general, "cms", cms_main, cms_options},
+#endif
+ {FT_general, "crl", crl_main, crl_options},
+ {FT_general, "crl2pkcs7", crl2pkcs7_main, crl2pkcs7_options},
+ {FT_general, "dgst", dgst_main, dgst_options},
+#ifndef OPENSSL_NO_DH
+ {FT_general, "dhparam", dhparam_main, dhparam_options},
+#endif
+#ifndef OPENSSL_NO_DSA
+ {FT_general, "dsa", dsa_main, dsa_options},
+#endif
+#ifndef OPENSSL_NO_DSA
+ {FT_general, "dsaparam", dsaparam_main, dsaparam_options},
+#endif
+#ifndef OPENSSL_NO_EC
+ {FT_general, "ec", ec_main, ec_options},
+#endif
+#ifndef OPENSSL_NO_EC
+ {FT_general, "ecparam", ecparam_main, ecparam_options},
+#endif
+ {FT_general, "enc", enc_main, enc_options},
+#ifndef OPENSSL_NO_ENGINE
+ {FT_general, "engine", engine_main, engine_options},
+#endif
+ {FT_general, "errstr", errstr_main, errstr_options},
+#ifndef OPENSSL_NO_DSA
+ {FT_general, "gendsa", gendsa_main, gendsa_options},
+#endif
+ {FT_general, "genpkey", genpkey_main, genpkey_options},
+#ifndef OPENSSL_NO_RSA
+ {FT_general, "genrsa", genrsa_main, genrsa_options},
+#endif
+ {FT_general, "help", help_main, help_options},
+ {FT_general, "list", list_main, list_options},
+ {FT_general, "nseq", nseq_main, nseq_options},
+#ifndef OPENSSL_NO_OCSP
+ {FT_general, "ocsp", ocsp_main, ocsp_options},
+#endif
+ {FT_general, "passwd", passwd_main, passwd_options},
+#ifndef OPENSSL_NO_DES
+ {FT_general, "pkcs12", pkcs12_main, pkcs12_options},
+#endif
+ {FT_general, "pkcs7", pkcs7_main, pkcs7_options},
+ {FT_general, "pkcs8", pkcs8_main, pkcs8_options},
+ {FT_general, "pkey", pkey_main, pkey_options},
+ {FT_general, "pkeyparam", pkeyparam_main, pkeyparam_options},
+ {FT_general, "pkeyutl", pkeyutl_main, pkeyutl_options},
+ {FT_general, "prime", prime_main, prime_options},
+ {FT_general, "rand", rand_main, rand_options},
+ {FT_general, "rehash", rehash_main, rehash_options},
+ {FT_general, "req", req_main, req_options},
+ {FT_general, "rsa", rsa_main, rsa_options},
+#ifndef OPENSSL_NO_RSA
+ {FT_general, "rsautl", rsautl_main, rsautl_options},
+#endif
+#ifndef OPENSSL_NO_SOCK
+ {FT_general, "s_client", s_client_main, s_client_options},
+#endif
+#ifndef OPENSSL_NO_SOCK
+ {FT_general, "s_server", s_server_main, s_server_options},
+#endif
+#ifndef OPENSSL_NO_SOCK
+ {FT_general, "s_time", s_time_main, s_time_options},
+#endif
+ {FT_general, "sess_id", sess_id_main, sess_id_options},
+ {FT_general, "smime", smime_main, smime_options},
+ {FT_general, "speed", speed_main, speed_options},
+ {FT_general, "spkac", spkac_main, spkac_options},
+#ifndef OPENSSL_NO_SRP
+ {FT_general, "srp", srp_main, srp_options},
+#endif
+ {FT_general, "storeutl", storeutl_main, storeutl_options},
+#ifndef OPENSSL_NO_TS
+ {FT_general, "ts", ts_main, ts_options},
+#endif
+ {FT_general, "verify", verify_main, verify_options},
+ {FT_general, "version", version_main, version_options},
+ {FT_general, "x509", x509_main, x509_options},
+#ifndef OPENSSL_NO_MD2
+ {FT_md, "md2", dgst_main},
+#endif
+#ifndef OPENSSL_NO_MD4
+ {FT_md, "md4", dgst_main},
+#endif
+ {FT_md, "md5", dgst_main},
+#ifndef OPENSSL_NO_GOST
+ {FT_md, "gost", dgst_main},
+#endif
+ {FT_md, "sha1", dgst_main},
+ {FT_md, "sha224", dgst_main},
+ {FT_md, "sha256", dgst_main},
+ {FT_md, "sha384", dgst_main},
+ {FT_md, "sha512", dgst_main},
+ {FT_md, "sha512-224", dgst_main},
+ {FT_md, "sha512-256", dgst_main},
+ {FT_md, "sha3-224", dgst_main},
+ {FT_md, "sha3-256", dgst_main},
+ {FT_md, "sha3-384", dgst_main},
+ {FT_md, "sha3-512", dgst_main},
+ {FT_md, "shake128", dgst_main},
+ {FT_md, "shake256", dgst_main},
+#ifndef OPENSSL_NO_MDC2
+ {FT_md, "mdc2", dgst_main},
+#endif
+#ifndef OPENSSL_NO_RMD160
+ {FT_md, "rmd160", dgst_main},
+#endif
+#ifndef OPENSSL_NO_BLAKE2
+ {FT_md, "blake2b512", dgst_main},
+#endif
+#ifndef OPENSSL_NO_BLAKE2
+ {FT_md, "blake2s256", dgst_main},
+#endif
+#ifndef OPENSSL_NO_SM3
+ {FT_md, "sm3", dgst_main},
+#endif
+ {FT_cipher, "aes-128-cbc", enc_main, enc_options},
+ {FT_cipher, "aes-128-ecb", enc_main, enc_options},
+ {FT_cipher, "aes-192-cbc", enc_main, enc_options},
+ {FT_cipher, "aes-192-ecb", enc_main, enc_options},
+ {FT_cipher, "aes-256-cbc", enc_main, enc_options},
+ {FT_cipher, "aes-256-ecb", enc_main, enc_options},
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-128-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-128-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-128-ctr", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-128-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-128-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-128-cfb1", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-128-cfb8", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-192-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-192-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-192-ctr", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-192-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-192-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-192-cfb1", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-192-cfb8", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-256-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-256-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-256-ctr", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-256-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-256-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-256-cfb1", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_ARIA
+ {FT_cipher, "aria-256-cfb8", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+ {FT_cipher, "camellia-128-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+ {FT_cipher, "camellia-128-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+ {FT_cipher, "camellia-192-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+ {FT_cipher, "camellia-192-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+ {FT_cipher, "camellia-256-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+ {FT_cipher, "camellia-256-ecb", enc_main, enc_options},
+#endif
+ {FT_cipher, "base64", enc_main, enc_options},
+#ifdef ZLIB
+ {FT_cipher, "zlib", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des3", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "desx", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_IDEA
+ {FT_cipher, "idea", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SEED
+ {FT_cipher, "seed", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC4
+ {FT_cipher, "rc4", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC4
+ {FT_cipher, "rc4-40", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC2
+ {FT_cipher, "rc2", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_BF
+ {FT_cipher, "bf", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAST
+ {FT_cipher, "cast", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC5
+ {FT_cipher, "rc5", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ede", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ede3", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ede-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ede3-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ede-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ede3-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ede-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_DES
+ {FT_cipher, "des-ede3-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_IDEA
+ {FT_cipher, "idea-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_IDEA
+ {FT_cipher, "idea-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_IDEA
+ {FT_cipher, "idea-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_IDEA
+ {FT_cipher, "idea-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SEED
+ {FT_cipher, "seed-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SEED
+ {FT_cipher, "seed-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SEED
+ {FT_cipher, "seed-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SEED
+ {FT_cipher, "seed-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC2
+ {FT_cipher, "rc2-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC2
+ {FT_cipher, "rc2-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC2
+ {FT_cipher, "rc2-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC2
+ {FT_cipher, "rc2-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC2
+ {FT_cipher, "rc2-64-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC2
+ {FT_cipher, "rc2-40-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_BF
+ {FT_cipher, "bf-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_BF
+ {FT_cipher, "bf-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_BF
+ {FT_cipher, "bf-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_BF
+ {FT_cipher, "bf-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAST
+ {FT_cipher, "cast5-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAST
+ {FT_cipher, "cast5-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAST
+ {FT_cipher, "cast5-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAST
+ {FT_cipher, "cast5-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_CAST
+ {FT_cipher, "cast-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC5
+ {FT_cipher, "rc5-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC5
+ {FT_cipher, "rc5-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC5
+ {FT_cipher, "rc5-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_RC5
+ {FT_cipher, "rc5-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SM4
+ {FT_cipher, "sm4-cbc", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SM4
+ {FT_cipher, "sm4-ecb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SM4
+ {FT_cipher, "sm4-cfb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SM4
+ {FT_cipher, "sm4-ofb", enc_main, enc_options},
+#endif
+#ifndef OPENSSL_NO_SM4
+ {FT_cipher, "sm4-ctr", enc_main, enc_options},
+#endif
+ {0, NULL, NULL}
+};
#endif
diff --git a/contrib/libs/openssl/patches/quictls-from-ms.patch b/contrib/libs/openssl/patches/quictls-from-ms.patch
new file mode 100644
index 00000000000..c885ba094f0
--- /dev/null
+++ b/contrib/libs/openssl/patches/quictls-from-ms.patch
@@ -0,0 +1,1657 @@
+commit a1710ceeba972d163108e8a86bba74b090c8f4c8
+merge: 22a7fd65ca59aa889ed516b87e9c752abc44f006 d0f8b7eff774f6436502af3a9685952c19bec76d
+author: xiwra
+date: 2024-10-11T04:10:50+03:00
+revision: 15072932
+
+ OpenSSL+quictls
+
+ applied patch from ms: <https://github.com/quictls/openssl/releases/tag/OpenSSL_1_1_1w-quic1>
+
+ needed to bring QUIC to smart devices
+
+ PoC with this patch checked here: <https://a.yandex-team.ru/review/6663586>
+
+ REVIEW: 6903903
+
+--- contrib/libs/openssl/include/openssl/bnerr.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/bnerr.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -72,6 +72,7 @@ int ERR_load_BN_strings(void);
+ # define BN_F_BN_SET_WORDS 144
+ # define BN_F_BN_STACK_PUSH 148
+ # define BN_F_BN_USUB 115
++# define BN_F_OSSL_BN_RSA_DO_UNBLIND 151
+
+ /*
+ * BN reason codes.
+--- contrib/libs/openssl/include/openssl/crypto.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/crypto.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -161,6 +161,10 @@ const char *OpenSSL_version(int type);
+ # define OPENSSL_DIR 4
+ # define OPENSSL_ENGINES_DIR 5
+
++# ifndef OPENSSL_NO_QUIC
++# define OPENSSL_INFO_QUIC 2000
++# endif
++
+ int OPENSSL_issetugid(void);
+
+ typedef void CRYPTO_EX_new (void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+--- contrib/libs/openssl/include/openssl/dh.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/dh.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -29,6 +29,9 @@ extern "C" {
+ # ifndef OPENSSL_DH_MAX_MODULUS_BITS
+ # define OPENSSL_DH_MAX_MODULUS_BITS 10000
+ # endif
++# ifndef OPENSSL_DH_CHECK_MAX_MODULUS_BITS
++# define OPENSSL_DH_CHECK_MAX_MODULUS_BITS 32768
++# endif
+
+ # define OPENSSL_DH_FIPS_MIN_MODULUS_BITS 1024
+
+--- contrib/libs/openssl/include/openssl/dherr.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/dherr.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -30,6 +30,7 @@ int ERR_load_DH_strings(void);
+ # define DH_F_COMPUTE_KEY 102
+ # define DH_F_DHPARAMS_PRINT_FP 101
+ # define DH_F_DH_BUILTIN_GENPARAMS 106
++# define DH_F_DH_CHECK 126
+ # define DH_F_DH_CHECK_EX 121
+ # define DH_F_DH_CHECK_PARAMS_EX 122
+ # define DH_F_DH_CHECK_PUB_KEY_EX 123
+--- contrib/libs/openssl/include/openssl/evp.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/evp.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -1324,6 +1324,10 @@ void EVP_PKEY_asn1_set_security_bits(EVP_PKEY_ASN1_METHOD *ameth,
+ */
+ # define EVP_PKEY_FLAG_SIGCTX_CUSTOM 4
+
++/* Used by Chromium/QUIC */
++# define X25519_PRIVATE_KEY_LEN 32
++# define X25519_PUBLIC_VALUE_LEN 32
++
+ const EVP_PKEY_METHOD *EVP_PKEY_meth_find(int type);
+ EVP_PKEY_METHOD *EVP_PKEY_meth_new(int id, int flags);
+ void EVP_PKEY_meth_get0_info(int *ppkey_id, int *pflags,
+--- contrib/libs/openssl/include/openssl/ossl_typ.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/ossl_typ.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -176,6 +176,8 @@ typedef struct ct_policy_eval_ctx_st CT_POLICY_EVAL_CTX;
+ typedef struct ossl_store_info_st OSSL_STORE_INFO;
+ typedef struct ossl_store_search_st OSSL_STORE_SEARCH;
+
++typedef struct ssl_quic_method_st SSL_QUIC_METHOD;
++
+ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \
+ defined(INTMAX_MAX) && defined(UINTMAX_MAX)
+ typedef intmax_t ossl_intmax_t;
+--- contrib/libs/openssl/include/openssl/ssl.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/ssl.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -1931,6 +1931,7 @@ int SSL_get_key_update_type(const SSL *s);
+ int SSL_renegotiate(SSL *s);
+ int SSL_renegotiate_abbreviated(SSL *s);
+ __owur int SSL_renegotiate_pending(const SSL *s);
++int SSL_new_session_ticket(SSL *s);
+ int SSL_shutdown(SSL *s);
+ __owur int SSL_verify_client_post_handshake(SSL *s);
+ void SSL_CTX_set_post_handshake_auth(SSL_CTX *ctx, int val);
+@@ -2442,6 +2443,66 @@ void SSL_set_allow_early_data_cb(SSL *s,
+ SSL_allow_early_data_cb_fn cb,
+ void *arg);
+
++# ifndef OPENSSL_NO_QUIC
++/*
++ * QUIC integration - The QUIC interface matches BoringSSL
++ *
++ * ssl_encryption_level_t represents a specific QUIC encryption level used to
++ * transmit handshake messages. BoringSSL has this as an 'enum'.
++ */
++typedef enum ssl_encryption_level_t {
++ ssl_encryption_initial = 0,
++ ssl_encryption_early_data,
++ ssl_encryption_handshake,
++ ssl_encryption_application
++} OSSL_ENCRYPTION_LEVEL;
++
++struct ssl_quic_method_st {
++ int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
++ const uint8_t *read_secret,
++ const uint8_t *write_secret, size_t secret_len);
++ int (*add_handshake_data)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
++ const uint8_t *data, size_t len);
++ int (*flush_flight)(SSL *ssl);
++ int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert);
++};
++
++__owur int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method);
++__owur int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method);
++__owur int SSL_set_quic_transport_params(SSL *ssl,
++ const uint8_t *params,
++ size_t params_len);
++void SSL_get_peer_quic_transport_params(const SSL *ssl,
++ const uint8_t **out_params,
++ size_t *out_params_len);
++__owur size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level);
++__owur OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl);
++__owur OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl);
++__owur int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
++ const uint8_t *data, size_t len);
++__owur int SSL_process_quic_post_handshake(SSL *ssl);
++
++__owur int SSL_is_quic(SSL *ssl);
++
++/* BoringSSL API */
++void SSL_set_quic_use_legacy_codepoint(SSL *ssl, int use_legacy);
++
++/*
++ * Set an explicit value that you want to use
++ * If 0 (default) the server will use the highest extenstion the client sent
++ * If 0 (default) the client will send both extensions
++ */
++void SSL_set_quic_transport_version(SSL *ssl, int version);
++__owur int SSL_get_quic_transport_version(const SSL *ssl);
++/* Returns the negotiated version, or -1 on error */
++__owur int SSL_get_peer_quic_transport_version(const SSL *ssl);
++
++int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c);
++
++void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled);
++
++# endif
++
+ # ifdef __cplusplus
+ }
+ # endif
+--- contrib/libs/openssl/include/openssl/sslerr.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/sslerr.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -71,6 +71,7 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_FINAL_KEY_SHARE 503
+ # define SSL_F_FINAL_MAXFRAGMENTLEN 557
+ # define SSL_F_FINAL_PSK 639
++# define SSL_F_FINAL_QUIC_TRANSPORT_PARAMS 3012
+ # define SSL_F_FINAL_RENEGOTIATE 483
+ # define SSL_F_FINAL_SERVER_NAME 558
+ # define SSL_F_FINAL_SIG_ALGS 497
+@@ -97,6 +98,9 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_PITEM_NEW 624
+ # define SSL_F_PQUEUE_NEW 625
+ # define SSL_F_PROCESS_KEY_SHARE_EXT 439
++# define SSL_F_QUIC_CHANGE_CIPHER_STATE 3000
++# define SSL_F_QUIC_GET_MESSAGE 3001
++# define SSL_F_QUIC_SET_ENCRYPTION_SECRETS 3002
+ # define SSL_F_READ_STATE_MACHINE 352
+ # define SSL_F_SET_CLIENT_CIPHERSUITE 540
+ # define SSL_F_SRP_GENERATE_CLIENT_MASTER_SECRET 595
+@@ -107,7 +111,9 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_SSL3_CTRL 213
+ # define SSL_F_SSL3_CTX_CTRL 133
+ # define SSL_F_SSL3_DIGEST_CACHED_RECORDS 293
++# define SSL_F_SSL3_DISPATCH_ALERT 3003
+ # define SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC 292
++# define SSL_F_SSL3_DO_WRITE 3004
+ # define SSL_F_SSL3_ENC 608
+ # define SSL_F_SSL3_FINAL_FINISH_MAC 285
+ # define SSL_F_SSL3_FINISH_MAC 587
+@@ -212,6 +218,8 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_SSL_PEEK 270
+ # define SSL_F_SSL_PEEK_EX 432
+ # define SSL_F_SSL_PEEK_INTERNAL 522
++# define SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE 3005
++# define SSL_F_SSL_PROVIDE_QUIC_DATA 3006
+ # define SSL_F_SSL_READ 223
+ # define SSL_F_SSL_READ_EARLY_DATA 529
+ # define SSL_F_SSL_READ_EX 434
+@@ -261,6 +269,7 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_SSL_WRITE_EARLY_FINISH 527
+ # define SSL_F_SSL_WRITE_EX 433
+ # define SSL_F_SSL_WRITE_INTERNAL 524
++# define SSL_F_STATEM_FLUSH 3007
+ # define SSL_F_STATE_MACHINE 353
+ # define SSL_F_TLS12_CHECK_PEER_SIGALG 333
+ # define SSL_F_TLS12_COPY_SIGALGS 533
+@@ -320,6 +329,8 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH 619
+ # define SSL_F_TLS_CONSTRUCT_CTOS_PSK 501
+ # define SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES 509
++# define SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS 3008
++# define SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT 3013
+ # define SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE 473
+ # define SSL_F_TLS_CONSTRUCT_CTOS_SCT 474
+ # define SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME 475
+@@ -359,6 +370,8 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN 548
+ # define SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG 457
+ # define SSL_F_TLS_CONSTRUCT_STOC_PSK 504
++# define SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS 3009
++# define SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS_DRAFT 3014
+ # define SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE 458
+ # define SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME 459
+ # define SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET 460
+@@ -384,6 +397,8 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH 620
+ # define SSL_F_TLS_PARSE_CTOS_PSK 505
+ # define SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES 572
++# define SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS 3010
++# define SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT 3015
+ # define SSL_F_TLS_PARSE_CTOS_RENEGOTIATE 464
+ # define SSL_F_TLS_PARSE_CTOS_SERVER_NAME 573
+ # define SSL_F_TLS_PARSE_CTOS_SESSION_TICKET 574
+@@ -402,6 +417,8 @@ int ERR_load_SSL_strings(void);
+ # define SSL_F_TLS_PARSE_STOC_MAXFRAGMENTLEN 581
+ # define SSL_F_TLS_PARSE_STOC_NPN 582
+ # define SSL_F_TLS_PARSE_STOC_PSK 502
++# define SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS 3011
++# define SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS_DRAFT 3016
+ # define SSL_F_TLS_PARSE_STOC_RENEGOTIATE 448
+ # define SSL_F_TLS_PARSE_STOC_SCT 564
+ # define SSL_F_TLS_PARSE_STOC_SERVER_NAME 583
+@@ -594,6 +611,7 @@ int ERR_load_SSL_strings(void);
+ # define SSL_R_MISSING_FATAL 256
+ # define SSL_R_MISSING_PARAMETERS 290
+ # define SSL_R_MISSING_PSK_KEX_MODES_EXTENSION 310
++# define SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION 801
+ # define SSL_R_MISSING_RSA_CERTIFICATE 168
+ # define SSL_R_MISSING_RSA_ENCRYPTING_CERT 169
+ # define SSL_R_MISSING_RSA_SIGNING_CERT 170
+@@ -765,6 +783,7 @@ int ERR_load_SSL_strings(void);
+ # define SSL_R_WRONG_CERTIFICATE_TYPE 383
+ # define SSL_R_WRONG_CIPHER_RETURNED 261
+ # define SSL_R_WRONG_CURVE 378
++# define SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED 800
+ # define SSL_R_WRONG_SIGNATURE_LENGTH 264
+ # define SSL_R_WRONG_SIGNATURE_SIZE 265
+ # define SSL_R_WRONG_SIGNATURE_TYPE 370
+--- contrib/libs/openssl/include/openssl/tls1.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/include/openssl/tls1.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -148,6 +148,10 @@ extern "C" {
+ /* Temporary extension type */
+ # define TLSEXT_TYPE_renegotiate 0xff01
+
++/* ExtensionType value from draft-ietf-quic-tls-27 */
++# define TLSEXT_TYPE_quic_transport_parameters_draft 0xffa5
++# define TLSEXT_TYPE_quic_transport_parameters 0x0039
++
+ # ifndef OPENSSL_NO_NEXTPROTONEG
+ /* This is not an IANA defined extension number */
+ # define TLSEXT_TYPE_next_proto_neg 13172
+--- contrib/libs/openssl/ssl/record/rec_layer_s3.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/record/rec_layer_s3.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -387,10 +387,12 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len,
+ s->rlayer.wnum = 0;
+
+ /*
+- * If we are supposed to be sending a KeyUpdate then go into init unless we
+- * have writes pending - in which case we should finish doing that first.
++ * If we are supposed to be sending a KeyUpdate or NewSessionTicket then go
++ * into init unless we have writes pending - in which case we should finish
++ * doing that first.
+ */
+- if (wb->left == 0 && s->key_update != SSL_KEY_UPDATE_NONE)
++ if (wb->left == 0 && (s->key_update != SSL_KEY_UPDATE_NONE
++ || s->ext.extra_tickets_expected > 0))
+ ossl_statem_set_in_init(s, 1);
+
+ /*
+--- contrib/libs/openssl/ssl/s3_msg.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/s3_msg.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -77,6 +77,16 @@ int ssl3_dispatch_alert(SSL *s)
+
+ s->s3->alert_dispatch = 0;
+ alertlen = 2;
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s)) {
++ if (!s->quic_method->send_alert(s, s->quic_write_level,
++ s->s3->send_alert[1])) {
++ SSLerr(SSL_F_SSL3_DISPATCH_ALERT, ERR_R_INTERNAL_ERROR);
++ return 0;
++ }
++ i = 1;
++ } else
++#endif
+ i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], &alertlen, 1, 0,
+ &written);
+ if (i <= 0) {
+--- contrib/libs/openssl/ssl/ssl_ciph.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/ssl_ciph.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -2161,3 +2161,37 @@ int ssl_cert_is_disabled(size_t idx)
+ return 1;
+ return 0;
+ }
++
++#ifndef OPENSSL_NO_QUIC
++int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c)
++{
++ switch (c->algorithm2 & (0xFF << TLS1_PRF_DGST_SHIFT)) {
++ default:
++ break;
++ case TLS1_PRF_SHA1_MD5: /* TLS1_PRF */
++ return NID_md5_sha1;
++ case TLS1_PRF_SHA256:
++ return NID_sha256;
++ case TLS1_PRF_SHA384:
++ return NID_sha384;
++ case TLS1_PRF_GOST94:
++ return NID_id_GostR3411_94_prf;
++ case TLS1_PRF_GOST12_256:
++ return NID_id_GostR3411_2012_256;
++ case TLS1_PRF_GOST12_512:
++ return NID_id_GostR3411_2012_512;
++ }
++ /* TLSv1.3 ciphers don't specify separate PRF */
++ switch (c->algorithm2 & SSL_HANDSHAKE_MAC_MASK) {
++ default:
++ break;
++ case SSL_HANDSHAKE_MAC_MD5_SHA1: /* SSL_HANDSHAKE_MAC_DEFAULT */
++ return NID_md5_sha1;
++ case SSL_HANDSHAKE_MAC_SHA256:
++ return NID_sha256;
++ case SSL_HANDSHAKE_MAC_SHA384:
++ return NID_sha384;
++ }
++ return NID_undef;
++}
++#endif
+--- contrib/libs/openssl/ssl/ssl_err.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/ssl_err.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -86,6 +86,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_MAXFRAGMENTLEN, 0),
+ "final_maxfragmentlen"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_PSK, 0), "final_psk"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_QUIC_TRANSPORT_PARAMS, 0),
++ "final_quic_transport_params"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_RENEGOTIATE, 0), "final_renegotiate"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_SERVER_NAME, 0), "final_server_name"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_SIG_ALGS, 0), "final_sig_algs"},
+@@ -113,6 +115,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ "ossl_statem_server_post_process_message"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_POST_WORK, 0),
+ "ossl_statem_server_post_work"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_PRE_WORK, 0),
++ "ossl_statem_server_pre_work"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_PROCESS_MESSAGE, 0),
+ "ossl_statem_server_process_message"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION, 0),
+@@ -123,6 +127,11 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_PITEM_NEW, 0), "pitem_new"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_PQUEUE_NEW, 0), "pqueue_new"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_PROCESS_KEY_SHARE_EXT, 0), ""},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_CHANGE_CIPHER_STATE, 0),
++ "quic_change_cipher_state"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_GET_MESSAGE, 0), "quic_get_message"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, 0),
++ "quic_set_encryption_secrets"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_READ_STATE_MACHINE, 0), "read_state_machine"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SET_CLIENT_CIPHERSUITE, 0),
+ "set_client_ciphersuite"},
+@@ -140,8 +149,11 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_CTX_CTRL, 0), "ssl3_ctx_ctrl"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DIGEST_CACHED_RECORDS, 0),
+ "ssl3_digest_cached_records"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DISPATCH_ALERT, 0),
++ "ssl3_dispatch_alert"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC, 0),
+ "ssl3_do_change_cipher_spec"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DO_WRITE, 0), "ssl3_do_write"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_ENC, 0), "ssl3_enc"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_FINAL_FINISH_MAC, 0),
+ "ssl3_final_finish_mac"},
+@@ -303,6 +315,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK, 0), "SSL_peek"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK_EX, 0), "SSL_peek_ex"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK_INTERNAL, 0), "ssl_peek_internal"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE, 0),
++ "SSL_process_quic_post_handshake"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PROVIDE_QUIC_DATA, 0),
++ "SSL_provide_quic_data"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_READ, 0), "SSL_read"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_READ_EARLY_DATA, 0),
+ "SSL_read_early_data"},
+@@ -379,6 +395,7 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EARLY_FINISH, 0), ""},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EX, 0), "SSL_write_ex"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_INTERNAL, 0), "ssl_write_internal"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_STATEM_FLUSH, 0), "statem_flush"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_STATE_MACHINE, 0), "state_machine"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS12_CHECK_PEER_SIGALG, 0),
+ "tls12_check_peer_sigalg"},
+@@ -480,6 +497,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ "tls_construct_ctos_psk"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES, 0),
+ "tls_construct_ctos_psk_kex_modes"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, 0),
++ "tls_construct_ctos_quic_transport_params"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT, 0),
++ "tls_construct_ctos_quic_transport_params_draft"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE, 0),
+ "tls_construct_ctos_renegotiate"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_SCT, 0),
+@@ -551,6 +572,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ "tls_construct_stoc_next_proto_neg"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_PSK, 0),
+ "tls_construct_stoc_psk"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, 0),
++ "tls_construct_stoc_quic_transport_params"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS_DRAFT, 0),
++ "tls_construct_stoc_quic_transport_params_draft"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE, 0),
+ "tls_construct_stoc_renegotiate"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME, 0),
+@@ -597,6 +622,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK, 0), "tls_parse_ctos_psk"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES, 0),
+ "tls_parse_ctos_psk_kex_modes"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, 0),
++ "tls_parse_ctos_quic_transport_params"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT, 0),
++ "tls_parse_ctos_quic_transport_params_draft"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_RENEGOTIATE, 0),
+ "tls_parse_ctos_renegotiate"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_SERVER_NAME, 0),
+@@ -629,6 +658,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
+ "tls_parse_stoc_maxfragmentlen"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_NPN, 0), "tls_parse_stoc_npn"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_PSK, 0), "tls_parse_stoc_psk"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, 0),
++ "tls_parse_stoc_quic_transport_params"},
++ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS_DRAFT, 0),
++ "tls_parse_stoc_quic_transport_params_draft"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_RENEGOTIATE, 0),
+ "tls_parse_stoc_renegotiate"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_SCT, 0), "tls_parse_stoc_sct"},
+@@ -951,6 +984,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_PARAMETERS), "missing parameters"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_PSK_KEX_MODES_EXTENSION),
+ "missing psk kex modes extension"},
++ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION),
++ "missing quic transport parameters extension"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_CERTIFICATE),
+ "missing rsa certificate"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_ENCRYPTING_CERT),
+@@ -1253,6 +1288,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CIPHER_RETURNED),
+ "wrong cipher returned"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CURVE), "wrong curve"},
++ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED),
++ "wrong encryption level received"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_LENGTH),
+ "wrong signature length"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_SIZE),
+--- contrib/libs/openssl/ssl/ssl_lib.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/ssl_lib.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -574,8 +574,57 @@ static void clear_ciphers(SSL *s)
+ ssl_clear_hash_ctx(&s->write_hash);
+ }
+
++#ifndef OPENSSL_NO_QUIC
+ int SSL_clear(SSL *s)
+ {
++ if (!SSL_clear_not_quic(s))
++ return 0;
++ return SSL_clear_quic(s);
++}
++
++int SSL_clear_quic(SSL *s)
++{
++ OPENSSL_free(s->ext.peer_quic_transport_params_draft);
++ s->ext.peer_quic_transport_params_draft = NULL;
++ s->ext.peer_quic_transport_params_draft_len = 0;
++ OPENSSL_free(s->ext.peer_quic_transport_params);
++ s->ext.peer_quic_transport_params = NULL;
++ s->ext.peer_quic_transport_params_len = 0;
++ s->quic_read_level = ssl_encryption_initial;
++ s->quic_write_level = ssl_encryption_initial;
++ s->quic_latest_level_received = ssl_encryption_initial;
++ while (s->quic_input_data_head != NULL) {
++ QUIC_DATA *qd;
++
++ qd = s->quic_input_data_head;
++ s->quic_input_data_head = qd->next;
++ OPENSSL_free(qd);
++ }
++ s->quic_input_data_tail = NULL;
++ BUF_MEM_free(s->quic_buf);
++ s->quic_buf = NULL;
++ s->quic_next_record_start = 0;
++ memset(s->client_hand_traffic_secret, 0, EVP_MAX_MD_SIZE);
++ memset(s->server_hand_traffic_secret, 0, EVP_MAX_MD_SIZE);
++ memset(s->client_early_traffic_secret, 0, EVP_MAX_MD_SIZE);
++ /*
++ * CONFIG - DON'T CLEAR
++ * s->ext.quic_transport_params
++ * s->ext.quic_transport_params_len
++ * s->quic_transport_version
++ * s->quic_method = NULL;
++ */
++ return 1;
++}
++#endif
++
++/* Keep this conditional very local */
++#ifndef OPENSSL_NO_QUIC
++int SSL_clear_not_quic(SSL *s)
++#else
++int SSL_clear(SSL *s)
++#endif
++{
+ if (s->method == NULL) {
+ SSLerr(SSL_F_SSL_CLEAR, SSL_R_NO_METHOD_SPECIFIED);
+ return 0;
+@@ -845,6 +894,10 @@ SSL *SSL_new(SSL_CTX *ctx)
+
+ s->job = NULL;
+
++#ifndef OPENSSL_NO_QUIC
++ s->quic_method = ctx->quic_method;
++#endif
++
+ #ifndef OPENSSL_NO_CT
+ if (!SSL_set_ct_validation_callback(s, ctx->ct_validation_callback,
+ ctx->ct_validation_callback_arg))
+@@ -1212,6 +1265,20 @@ void SSL_free(SSL *s)
+ OPENSSL_free(s->pha_context);
+ EVP_MD_CTX_free(s->pha_dgst);
+
++#ifndef OPENSSL_NO_QUIC
++ OPENSSL_free(s->ext.quic_transport_params);
++ OPENSSL_free(s->ext.peer_quic_transport_params_draft);
++ OPENSSL_free(s->ext.peer_quic_transport_params);
++ BUF_MEM_free(s->quic_buf);
++ while (s->quic_input_data_head != NULL) {
++ QUIC_DATA *qd;
++
++ qd = s->quic_input_data_head;
++ s->quic_input_data_head = qd->next;
++ OPENSSL_free(qd);
++ }
++#endif
++
+ sk_X509_NAME_pop_free(s->ca_names, X509_NAME_free);
+ sk_X509_NAME_pop_free(s->client_ca_names, X509_NAME_free);
+
+@@ -1747,6 +1814,12 @@ static int ssl_io_intern(void *vargs)
+
+ int ssl_read_internal(SSL *s, void *buf, size_t num, size_t *readbytes)
+ {
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s)) {
++ SSLerr(SSL_F_SSL_READ_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
++ return -1;
++ }
++#endif
+ if (s->handshake_func == NULL) {
+ SSLerr(SSL_F_SSL_READ_INTERNAL, SSL_R_UNINITIALIZED);
+ return -1;
+@@ -1879,6 +1952,12 @@ int SSL_get_early_data_status(const SSL *s)
+
+ static int ssl_peek_internal(SSL *s, void *buf, size_t num, size_t *readbytes)
+ {
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s)) {
++ SSLerr(SSL_F_SSL_PEEK_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
++ return -1;
++ }
++#endif
+ if (s->handshake_func == NULL) {
+ SSLerr(SSL_F_SSL_PEEK_INTERNAL, SSL_R_UNINITIALIZED);
+ return -1;
+@@ -1939,6 +2018,12 @@ int SSL_peek_ex(SSL *s, void *buf, size_t num, size_t *readbytes)
+
+ int ssl_write_internal(SSL *s, const void *buf, size_t num, size_t *written)
+ {
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s)) {
++ SSLerr(SSL_F_SSL_WRITE_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
++ return -1;
++ }
++#endif
+ if (s->handshake_func == NULL) {
+ SSLerr(SSL_F_SSL_WRITE_INTERNAL, SSL_R_UNINITIALIZED);
+ return -1;
+@@ -2196,6 +2281,19 @@ int SSL_renegotiate_pending(const SSL *s)
+ return (s->renegotiate != 0);
+ }
+
++int SSL_new_session_ticket(SSL *s)
++{
++ /* If we are in init because we're sending tickets, okay to send more. */
++ if ((SSL_in_init(s) && s->ext.extra_tickets_expected == 0)
++ || SSL_IS_FIRST_HANDSHAKE(s) || !s->server
++ || !SSL_IS_TLS13(s))
++ return 0;
++ s->ext.extra_tickets_expected++;
++ if (s->rlayer.wbuf[0].left == 0 && !SSL_in_init(s))
++ ossl_statem_set_in_init(s, 1);
++ return 1;
++}
++
+ long SSL_ctrl(SSL *s, int cmd, long larg, void *parg)
+ {
+ long l;
+@@ -3635,6 +3733,11 @@ int SSL_get_error(const SSL *s, int i)
+ }
+
+ if (SSL_want_read(s)) {
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s)) {
++ return SSL_ERROR_WANT_READ;
++ }
++#endif
+ bio = SSL_get_rbio(s);
+ if (BIO_should_read(bio))
+ return SSL_ERROR_WANT_READ;
+@@ -3732,6 +3835,21 @@ int SSL_do_handshake(SSL *s)
+ ret = s->handshake_func(s);
+ }
+ }
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s) && ret == 1) {
++ if (s->server) {
++ if (s->early_data_state == SSL_EARLY_DATA_ACCEPTING) {
++ s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
++ s->rwstate = SSL_READING;
++ ret = 0;
++ }
++ } else if (s->early_data_state == SSL_EARLY_DATA_CONNECTING) {
++ s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY;
++ s->rwstate = SSL_READING;
++ ret = 0;
++ }
++ }
++#endif
+ return ret;
+ }
+
+--- contrib/libs/openssl/ssl/ssl_local.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/ssl_local.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -315,6 +315,13 @@
+ /* Flag used on OpenSSL ciphersuite ids to indicate they are for SSLv3+ */
+ # define SSL3_CK_CIPHERSUITE_FLAG 0x03000000
+
++/* Check if an SSL structure is using QUIC (which uses TLSv1.3) */
++# ifndef OPENSSL_NO_QUIC
++# define SSL_IS_QUIC(s) (s->quic_method != NULL)
++# else
++# define SSL_IS_QUIC(s) 0
++# endif
++
+ /* Check if an SSL structure is using DTLS */
+ # define SSL_IS_DTLS(s) (s->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_DTLS)
+
+@@ -714,6 +721,8 @@ typedef enum tlsext_index_en {
+ TLSEXT_IDX_cryptopro_bug,
+ TLSEXT_IDX_early_data,
+ TLSEXT_IDX_certificate_authorities,
++ TLSEXT_IDX_quic_transport_params_draft,
++ TLSEXT_IDX_quic_transport_params,
+ TLSEXT_IDX_padding,
+ TLSEXT_IDX_psk,
+ /* Dummy index - must always be the last entry */
+@@ -1063,8 +1072,25 @@ struct ssl_ctx_st {
+
+ /* Do we advertise Post-handshake auth support? */
+ int pha_enabled;
++
++#ifndef OPENSSL_NO_QUIC
++ const SSL_QUIC_METHOD *quic_method;
++#endif
+ };
+
++typedef struct cert_pkey_st CERT_PKEY;
++
++#ifndef OPENSSL_NO_QUIC
++struct quic_data_st {
++ struct quic_data_st *next;
++ OSSL_ENCRYPTION_LEVEL level;
++ size_t start; /* offset into quic_buf->data */
++ size_t length;
++};
++typedef struct quic_data_st QUIC_DATA;
++int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level);
++#endif
++
+ struct ssl_st {
+ /*
+ * protocol version (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION,
+@@ -1152,6 +1178,11 @@ struct ssl_st {
+ unsigned char handshake_traffic_hash[EVP_MAX_MD_SIZE];
+ unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE];
+ unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE];
++# ifndef OPENSSL_NO_QUIC
++ unsigned char client_hand_traffic_secret[EVP_MAX_MD_SIZE];
++ unsigned char server_hand_traffic_secret[EVP_MAX_MD_SIZE];
++ unsigned char client_early_traffic_secret[EVP_MAX_MD_SIZE];
++# endif
+ unsigned char exporter_master_secret[EVP_MAX_MD_SIZE];
+ unsigned char early_exporter_master_secret[EVP_MAX_MD_SIZE];
+ EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
+@@ -1289,6 +1320,8 @@ struct ssl_st {
+
+ /* RFC4507 session ticket expected to be received or sent */
+ int ticket_expected;
++ /* TLS 1.3 tickets requested by the application. */
++ int extra_tickets_expected;
+ # ifndef OPENSSL_NO_EC
+ size_t ecpointformats_len;
+ /* our list */
+@@ -1364,8 +1397,35 @@ struct ssl_st {
+ * selected.
+ */
+ int tick_identity;
++
++#ifndef OPENSSL_NO_QUIC
++ uint8_t *quic_transport_params;
++ size_t quic_transport_params_len;
++ uint8_t *peer_quic_transport_params_draft;
++ size_t peer_quic_transport_params_draft_len;
++ uint8_t *peer_quic_transport_params;
++ size_t peer_quic_transport_params_len;
++#endif
+ } ext;
+
++#ifndef OPENSSL_NO_QUIC
++ OSSL_ENCRYPTION_LEVEL quic_read_level;
++ OSSL_ENCRYPTION_LEVEL quic_write_level;
++ OSSL_ENCRYPTION_LEVEL quic_latest_level_received;
++ /*
++ * defaults to 0, but can be set to:
++ * - TLSEXT_TYPE_quic_transport_parameters_draft
++ * - TLSEXT_TYPE_quic_transport_parameters
++ * Client: if 0, send both
++ * Server: if 0, use same version as client sent
++ */
++ int quic_transport_version;
++ BUF_MEM *quic_buf; /* buffer incoming handshake messages */
++ QUIC_DATA *quic_input_data_head;
++ QUIC_DATA *quic_input_data_tail;
++ size_t quic_next_record_start;
++ const SSL_QUIC_METHOD *quic_method;
++#endif
+ /*
+ * Parsed form of the ClientHello, kept around across client_hello_cb
+ * calls.
+@@ -2660,6 +2720,11 @@ void custom_exts_free(custom_ext_methods *exts);
+
+ void ssl_comp_free_compression_methods_int(void);
+
++#ifndef OPENSSL_NO_QUIC
++__owur int SSL_clear_not_quic(SSL *s);
++__owur int SSL_clear_quic(SSL *s);
++#endif
++
+ /* ssl_mcnf.c */
+ void ssl_ctx_system_config(SSL_CTX *ctx);
+
+--- contrib/libs/openssl/ssl/statem/extensions.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/statem/extensions.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -58,6 +58,11 @@ static int final_early_data(SSL *s, unsigned int context, int sent);
+ static int final_maxfragmentlen(SSL *s, unsigned int context, int sent);
+ static int init_post_handshake_auth(SSL *s, unsigned int context);
+ static int final_psk(SSL *s, unsigned int context, int sent);
++#ifndef OPENSSL_NO_QUIC
++static int init_quic_transport_params(SSL *s, unsigned int context);
++static int final_quic_transport_params_draft(SSL *s, unsigned int context, int sent);
++static int final_quic_transport_params(SSL *s, unsigned int context, int sent);
++#endif
+
+ /* Structure to define a built-in extension */
+ typedef struct extensions_definition_st {
+@@ -377,6 +382,29 @@ static const EXTENSION_DEFINITION ext_defs[] = {
+ tls_construct_certificate_authorities,
+ tls_construct_certificate_authorities, NULL,
+ },
++#ifndef OPENSSL_NO_QUIC
++ {
++ TLSEXT_TYPE_quic_transport_parameters_draft,
++ SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
++ | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
++ init_quic_transport_params,
++ tls_parse_ctos_quic_transport_params_draft, tls_parse_stoc_quic_transport_params_draft,
++ tls_construct_stoc_quic_transport_params_draft, tls_construct_ctos_quic_transport_params_draft,
++ final_quic_transport_params_draft,
++ },
++ {
++ TLSEXT_TYPE_quic_transport_parameters,
++ SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
++ | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
++ init_quic_transport_params,
++ tls_parse_ctos_quic_transport_params, tls_parse_stoc_quic_transport_params,
++ tls_construct_stoc_quic_transport_params, tls_construct_ctos_quic_transport_params,
++ final_quic_transport_params,
++ },
++#else
++ INVALID_EXTENSION,
++ INVALID_EXTENSION,
++#endif
+ {
+ /* Must be immediately before pre_shared_key */
+ TLSEXT_TYPE_padding,
+@@ -1745,3 +1773,45 @@ static int final_psk(SSL *s, unsigned int context, int sent)
+
+ return 1;
+ }
++
++#ifndef OPENSSL_NO_QUIC
++static int init_quic_transport_params(SSL *s, unsigned int context)
++{
++ return 1;
++}
++
++static int final_quic_transport_params_draft(SSL *s, unsigned int context,
++ int sent)
++{
++ return 1;
++}
++
++static int final_quic_transport_params(SSL *s, unsigned int context, int sent)
++{
++ /* called after final_quic_transport_params_draft */
++ if (SSL_IS_QUIC(s)) {
++ if (s->ext.peer_quic_transport_params_len == 0
++ && s->ext.peer_quic_transport_params_draft_len == 0) {
++ SSLfatal(s, SSL_AD_MISSING_EXTENSION,
++ SSL_F_FINAL_QUIC_TRANSPORT_PARAMS,
++ SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION);
++ return 0;
++ }
++ /* if we got both, discard the one we can't use */
++ if (s->ext.peer_quic_transport_params_len != 0
++ && s->ext.peer_quic_transport_params_draft_len != 0) {
++ if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters_draft) {
++ OPENSSL_free(s->ext.peer_quic_transport_params);
++ s->ext.peer_quic_transport_params = NULL;
++ s->ext.peer_quic_transport_params_len = 0;
++ } else {
++ OPENSSL_free(s->ext.peer_quic_transport_params_draft);
++ s->ext.peer_quic_transport_params_draft = NULL;
++ s->ext.peer_quic_transport_params_draft_len = 0;
++ }
++ }
++ }
++
++ return 1;
++}
++#endif
+--- contrib/libs/openssl/ssl/statem/extensions_clnt.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/statem/extensions_clnt.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -1228,7 +1228,49 @@ EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt,
+ #endif
+ }
+
++#ifndef OPENSSL_NO_QUIC
++EXT_RETURN tls_construct_ctos_quic_transport_params_draft(SSL *s, WPACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx)
++{
++ if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters
++ || s->ext.quic_transport_params == NULL
++ || s->ext.quic_transport_params_len == 0) {
++ return EXT_RETURN_NOT_SENT;
++ }
+
++ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters_draft)
++ || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params,
++ s->ext.quic_transport_params_len)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT, ERR_R_INTERNAL_ERROR);
++ return EXT_RETURN_FAIL;
++ }
++
++ return EXT_RETURN_SENT;
++}
++
++EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx)
++{
++ if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters_draft
++ || s->ext.quic_transport_params == NULL
++ || s->ext.quic_transport_params_len == 0) {
++ return EXT_RETURN_NOT_SENT;
++ }
++
++ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters)
++ || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params,
++ s->ext.quic_transport_params_len)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR);
++ return EXT_RETURN_FAIL;
++ }
++
++ return EXT_RETURN_SENT;
++}
++#endif
+ /*
+ * Parse the server's renegotiation binding and abort if it's not right
+ */
+@@ -1938,6 +1980,18 @@ int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context,
+ return 0;
+ }
+
++#ifndef OPENSSL_NO_QUIC
++ /*
++ * QUIC server must send 0xFFFFFFFF or it's a PROTOCOL_VIOLATION
++ * per draft-ietf-quic-tls-27 S4.5
++ */
++ if (SSL_IS_QUIC(s) && max_early_data != 0xFFFFFFFF) {
++ SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_EARLY_DATA,
++ SSL_R_INVALID_MAX_EARLY_DATA);
++ return 0;
++ }
++#endif
++
+ s->session->ext.max_early_data = max_early_data;
+
+ return 1;
+@@ -2025,3 +2079,40 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
+
+ return 1;
+ }
++#ifndef OPENSSL_NO_QUIC
++int tls_parse_stoc_quic_transport_params_draft(SSL *s, PACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx)
++{
++ OPENSSL_free(s->ext.peer_quic_transport_params_draft);
++ s->ext.peer_quic_transport_params_draft = NULL;
++ s->ext.peer_quic_transport_params_draft_len = 0;
++
++ if (!PACKET_memdup(pkt,
++ &s->ext.peer_quic_transport_params_draft,
++ &s->ext.peer_quic_transport_params_draft_len)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS_DRAFT, ERR_R_INTERNAL_ERROR);
++ return 0;
++ }
++ return 1;
++}
++
++int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context,
++ X509 *x, size_t chainidx)
++{
++ OPENSSL_free(s->ext.peer_quic_transport_params);
++ s->ext.peer_quic_transport_params = NULL;
++ s->ext.peer_quic_transport_params_len = 0;
++
++ if (!PACKET_memdup(pkt,
++ &s->ext.peer_quic_transport_params,
++ &s->ext.peer_quic_transport_params_len)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS,
++ ERR_R_INTERNAL_ERROR);
++ return 0;
++ }
++ return 1;
++}
++#endif
+--- contrib/libs/openssl/ssl/statem/extensions_srvr.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/statem/extensions_srvr.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -1302,6 +1302,43 @@ int tls_parse_ctos_post_handshake_auth(SSL *s, PACKET *pkt, unsigned int context
+ return 1;
+ }
+
++#ifndef OPENSSL_NO_QUIC
++int tls_parse_ctos_quic_transport_params_draft(SSL *s, PACKET *pkt, unsigned int context,
++ X509 *x, size_t chainidx)
++{
++ OPENSSL_free(s->ext.peer_quic_transport_params_draft);
++ s->ext.peer_quic_transport_params_draft = NULL;
++ s->ext.peer_quic_transport_params_draft_len = 0;
++
++ if (!PACKET_memdup(pkt,
++ &s->ext.peer_quic_transport_params_draft,
++ &s->ext.peer_quic_transport_params_draft_len)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT, ERR_R_INTERNAL_ERROR);
++ return 0;
++ }
++ return 1;
++}
++
++int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context,
++ X509 *x, size_t chainidx)
++{
++ OPENSSL_free(s->ext.peer_quic_transport_params);
++ s->ext.peer_quic_transport_params = NULL;
++ s->ext.peer_quic_transport_params_len = 0;
++
++ if (!PACKET_memdup(pkt,
++ &s->ext.peer_quic_transport_params,
++ &s->ext.peer_quic_transport_params_len)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS,
++ ERR_R_INTERNAL_ERROR);
++ return 0;
++ }
++ return 1;
++}
++#endif
++
+ /*
+ * Add the server's renegotiation binding
+ */
+@@ -1932,12 +1969,20 @@ EXT_RETURN tls_construct_stoc_early_data(SSL *s, WPACKET *pkt,
+ size_t chainidx)
+ {
+ if (context == SSL_EXT_TLS1_3_NEW_SESSION_TICKET) {
++ uint32_t max_early_data = s->max_early_data;
++
+ if (s->max_early_data == 0)
+ return EXT_RETURN_NOT_SENT;
+
++#ifndef OPENSSL_NO_QUIC
++ /* QUIC server must always send 0xFFFFFFFF, per draft-ietf-quic-tls-27 S4.5 */
++ if (SSL_IS_QUIC(s))
++ max_early_data = 0xFFFFFFFF;
++#endif
++
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data)
+ || !WPACKET_start_sub_packet_u16(pkt)
+- || !WPACKET_put_bytes_u32(pkt, s->max_early_data)
++ || !WPACKET_put_bytes_u32(pkt, max_early_data)
+ || !WPACKET_close(pkt)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_STOC_EARLY_DATA, ERR_R_INTERNAL_ERROR);
+@@ -1978,3 +2023,51 @@ EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context,
+
+ return EXT_RETURN_SENT;
+ }
++
++#ifndef OPENSSL_NO_QUIC
++EXT_RETURN tls_construct_stoc_quic_transport_params_draft(SSL *s, WPACKET *pkt,
++ unsigned int context,
++ X509 *x,
++ size_t chainidx)
++{
++ if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters
++ || s->ext.peer_quic_transport_params_draft_len == 0
++ || s->ext.quic_transport_params == NULL
++ || s->ext.quic_transport_params_len == 0) {
++ return EXT_RETURN_NOT_SENT;
++ }
++
++ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters_draft)
++ || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params,
++ s->ext.quic_transport_params_len)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS_DRAFT, ERR_R_INTERNAL_ERROR);
++ return EXT_RETURN_FAIL;
++ }
++
++ return EXT_RETURN_SENT;
++}
++
++EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx)
++{
++ if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters_draft
++ || s->ext.peer_quic_transport_params_len == 0
++ || s->ext.quic_transport_params == NULL
++ || s->ext.quic_transport_params_len == 0) {
++ return EXT_RETURN_NOT_SENT;
++ }
++
++ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters)
++ || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params,
++ s->ext.quic_transport_params_len)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS,
++ ERR_R_INTERNAL_ERROR);
++ return EXT_RETURN_FAIL;
++ }
++
++ return EXT_RETURN_SENT;
++}
++#endif
+--- contrib/libs/openssl/ssl/statem/statem.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/statem/statem.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -319,8 +319,13 @@ static int state_machine(SSL *s, int server)
+ * If we are stateless then we already called SSL_clear() - don't do
+ * it again and clear the STATELESS flag itself.
+ */
++#ifndef OPENSSL_NO_QUIC
++ if ((s->s3->flags & TLS1_FLAGS_STATELESS) == 0 && !SSL_clear_not_quic(s))
++ return -1;
++#else
+ if ((s->s3->flags & TLS1_FLAGS_STATELESS) == 0 && !SSL_clear(s))
+ return -1;
++#endif
+ }
+ #ifndef OPENSSL_NO_SCTP
+ if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s))) {
+@@ -575,6 +580,11 @@ static SUB_STATE_RETURN read_state_machine(SSL *s)
+ * In DTLS we get the whole message in one go - header and body
+ */
+ ret = dtls_get_message(s, &mt, &len);
++#ifndef OPENSSL_NO_QUIC
++ } else if (SSL_IS_QUIC(s)) {
++ /* QUIC behaves like DTLS -- all in one go. */
++ ret = quic_get_message(s, &mt, &len);
++#endif
+ } else {
+ ret = tls_get_message_header(s, &mt);
+ }
+@@ -604,8 +614,8 @@ static SUB_STATE_RETURN read_state_machine(SSL *s)
+ return SUB_STATE_ERROR;
+ }
+
+- /* dtls_get_message already did this */
+- if (!SSL_IS_DTLS(s)
++ /* dtls_get_message/quic_get_message already did this */
++ if (!SSL_IS_DTLS(s) && !SSL_IS_QUIC(s)
+ && s->s3->tmp.message_size > 0
+ && !grow_init_buf(s, s->s3->tmp.message_size
+ + SSL3_HM_HEADER_LENGTH)) {
+@@ -618,8 +628,8 @@ static SUB_STATE_RETURN read_state_machine(SSL *s)
+ /* Fall through */
+
+ case READ_STATE_BODY:
+- if (!SSL_IS_DTLS(s)) {
+- /* We already got this above for DTLS */
++ if (!SSL_IS_DTLS(s) && !SSL_IS_QUIC(s)) {
++ /* We already got this above for DTLS & QUIC */
+ ret = tls_get_message_body(s, &len);
+ if (ret == 0) {
+ /* Could be non-blocking IO */
+@@ -900,6 +910,14 @@ static SUB_STATE_RETURN write_state_machine(SSL *s)
+ int statem_flush(SSL *s)
+ {
+ s->rwstate = SSL_WRITING;
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s)) {
++ if (!s->quic_method->flush_flight(s)) {
++ SSLerr(SSL_F_STATEM_FLUSH, ERR_R_INTERNAL_ERROR);
++ return 0;
++ }
++ } else
++#endif
+ if (BIO_flush(s->wbio) <= 0) {
+ return 0;
+ }
+--- contrib/libs/openssl/ssl/statem/statem_clnt.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/statem/statem_clnt.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -909,6 +909,14 @@ int ossl_statem_client_construct_message(SSL *s, WPACKET *pkt,
+ break;
+
+ case TLS_ST_CW_END_OF_EARLY_DATA:
++#ifndef OPENSSL_NO_QUIC
++ /* QUIC does not send EndOfEarlyData, draft-ietf-quic-tls-24 S8.3 */
++ if (SSL_IS_QUIC(s)) {
++ *confunc = NULL;
++ *mt = SSL3_MT_DUMMY;
++ break;
++ }
++#endif
+ *confunc = tls_construct_end_of_early_data;
+ *mt = SSL3_MT_END_OF_EARLY_DATA;
+ break;
+--- contrib/libs/openssl/ssl/statem/statem_lib.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/statem/statem_lib.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -43,8 +43,28 @@ int ssl3_do_write(SSL *s, int type)
+ int ret;
+ size_t written = 0;
+
+- ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off],
+- s->init_num, &written);
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s)) {
++ if (type == SSL3_RT_HANDSHAKE) {
++ ret = s->quic_method->add_handshake_data(s, s->quic_write_level,
++ (const uint8_t*)&s->init_buf->data[s->init_off],
++ s->init_num);
++ if (!ret) {
++ ret = -1;
++ /* QUIC can't sent anything out sice the above failed */
++ SSLerr(SSL_F_SSL3_DO_WRITE, ERR_R_INTERNAL_ERROR);
++ } else {
++ written = s->init_num;
++ }
++ } else {
++ /* QUIC doesn't use ChangeCipherSpec */
++ ret = -1;
++ SSLerr(SSL_F_SSL3_DO_WRITE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
++ }
++ } else
++#endif
++ ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off],
++ s->init_num, &written);
+ if (ret < 0)
+ return -1;
+ if (type == SSL3_RT_HANDSHAKE)
+@@ -610,6 +630,14 @@ int tls_construct_finished(SSL *s, WPACKET *pkt)
+
+ int tls_construct_key_update(SSL *s, WPACKET *pkt)
+ {
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_is_quic(s)) {
++ /* TLS KeyUpdate is not used for QUIC, so this is an error. */
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_KEY_UPDATE,
++ ERR_R_INTERNAL_ERROR);
++ return 0;
++ }
++#endif
+ if (!WPACKET_put_bytes_u8(pkt, s->key_update)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_KEY_UPDATE,
+ ERR_R_INTERNAL_ERROR);
+@@ -634,6 +662,14 @@ MSG_PROCESS_RETURN tls_process_key_update(SSL *s, PACKET *pkt)
+ return MSG_PROCESS_ERROR;
+ }
+
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_is_quic(s)) {
++ SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_TLS_PROCESS_KEY_UPDATE,
++ SSL_R_UNEXPECTED_MESSAGE);
++ return MSG_PROCESS_ERROR;
++ }
++#endif
++
+ if (!PACKET_get_1(pkt, &updatetype)
+ || PACKET_remaining(pkt) != 0) {
+ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_KEY_UPDATE,
+--- contrib/libs/openssl/ssl/statem/statem_local.h (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/statem/statem_local.h (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -95,6 +95,9 @@ WORK_STATE ossl_statem_server_post_process_message(SSL *s, WORK_STATE wst);
+ __owur int tls_get_message_header(SSL *s, int *mt);
+ __owur int tls_get_message_body(SSL *s, size_t *len);
+ __owur int dtls_get_message(SSL *s, int *mt, size_t *len);
++#ifndef OPENSSL_NO_QUIC
++__owur int quic_get_message(SSL *s, int *mt, size_t *len);
++#endif
+
+ /* Message construction and processing functions */
+ __owur int tls_process_initial_server_flight(SSL *s);
+@@ -238,6 +241,14 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
+ size_t chainidx);
+ int tls_parse_ctos_post_handshake_auth(SSL *, PACKET *pkt, unsigned int context,
+ X509 *x, size_t chainidx);
++#ifndef OPENSSL_NO_QUIC
++int tls_parse_ctos_quic_transport_params_draft(SSL *s, PACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx);
++
++int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context,
++ X509 *x, size_t chainidx);
++#endif
+
+ EXT_RETURN tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt,
+ unsigned int context, X509 *x,
+@@ -300,6 +311,16 @@ EXT_RETURN tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt,
+ size_t chainidx);
+ EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context,
+ X509 *x, size_t chainidx);
++#ifndef OPENSSL_NO_QUIC
++EXT_RETURN tls_construct_stoc_quic_transport_params_draft(SSL *s, WPACKET *pkt,
++ unsigned int context,
++ X509 *x,
++ size_t chainidx);
++
++EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx);
++#endif
+
+ /* Client Extension processing */
+ EXT_RETURN tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, unsigned int context,
+@@ -370,6 +391,15 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
+ X509 *x, size_t chainidx);
+ EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, unsigned int context,
+ X509 *x, size_t chainidx);
++#ifndef OPENSSL_NO_QUIC
++EXT_RETURN tls_construct_ctos_quic_transport_params_draft(SSL *s, WPACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx);
++
++EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx);
++#endif
+
+ int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context,
+ X509 *x, size_t chainidx);
+@@ -415,6 +445,14 @@ int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
+ size_t chainidx);
+ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
+ size_t chainidx);
++#ifndef OPENSSL_NO_QUIC
++int tls_parse_stoc_quic_transport_params_draft(SSL *s, PACKET *pkt,
++ unsigned int context, X509 *x,
++ size_t chainidx);
++
++int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context,
++ X509 *x, size_t chainidx);
++#endif
+
+ int tls_handle_alpn(SSL *s);
+
+--- contrib/libs/openssl/ssl/statem/statem_srvr.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/statem/statem_srvr.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -72,7 +72,8 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt)
+ return 1;
+ }
+ break;
+- } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) {
++ } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED
++ && !SSL_IS_QUIC(s)) {
+ if (mt == SSL3_MT_END_OF_EARLY_DATA) {
+ st->hand_state = TLS_ST_SR_END_OF_EARLY_DATA;
+ return 1;
+@@ -436,6 +437,10 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
+ st->hand_state = TLS_ST_SW_CERT_REQ;
+ return WRITE_TRAN_CONTINUE;
+ }
++ if (s->ext.extra_tickets_expected > 0) {
++ st->hand_state = TLS_ST_SW_SESSION_TICKET;
++ return WRITE_TRAN_CONTINUE;
++ }
+ /* Try to read from the client instead */
+ return WRITE_TRAN_FINISHED;
+
+@@ -526,7 +531,9 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
+ * Following an initial handshake we send the number of tickets we have
+ * been configured for.
+ */
+- if (s->hit || s->num_tickets <= s->sent_tickets) {
++ if (!SSL_IS_FIRST_HANDSHAKE(s) && s->ext.extra_tickets_expected > 0) {
++ return WRITE_TRAN_CONTINUE;
++ } else if (s->hit || s->num_tickets <= s->sent_tickets) {
+ /* We've written enough tickets out. */
+ st->hand_state = TLS_ST_OK;
+ }
+@@ -722,7 +729,8 @@ WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst)
+ return WORK_FINISHED_CONTINUE;
+
+ case TLS_ST_SW_SESSION_TICKET:
+- if (SSL_IS_TLS13(s) && s->sent_tickets == 0) {
++ if (SSL_IS_TLS13(s) && s->sent_tickets == 0
++ && s->ext.extra_tickets_expected == 0) {
+ /*
+ * Actually this is the end of the handshake, but we're going
+ * straight into writing the session ticket out. So we finish off
+@@ -964,6 +972,16 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
+ SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_WRITE))
+ /* SSLfatal() already called */
+ return WORK_ERROR;
++
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s) && s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) {
++ s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
++ if (!s->method->ssl3_enc->change_cipher_state(
++ s, SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_READ))
++ /* SSLfatal() already called */
++ return WORK_ERROR;
++ }
++#endif
+ }
+ break;
+
+@@ -1578,6 +1596,16 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
+ goto err;
+ }
+ }
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s)) {
++ /* Any other QUIC checks on ClientHello here */
++ if (clienthello->session_id_len > 0) {
++ SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PROCESS_CLIENT_HELLO,
++ SSL_R_LENGTH_MISMATCH);
++ goto err;
++ }
++ }
++#endif
+ }
+
+ if (!PACKET_copy_all(&compression, clienthello->compressions,
+@@ -4183,10 +4211,13 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
+ /*
+ * Increment both |sent_tickets| and |next_ticket_nonce|. |sent_tickets|
+ * gets reset to 0 if we send more tickets following a post-handshake
+- * auth, but |next_ticket_nonce| does not.
++ * auth, but |next_ticket_nonce| does not. If we're sending extra
++ * tickets, decrement the count of pending extra tickets.
+ */
+ s->sent_tickets++;
+ s->next_ticket_nonce++;
++ if (s->ext.extra_tickets_expected > 0)
++ s->ext.extra_tickets_expected--;
+ ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
+ }
+
+--- contrib/libs/openssl/ssl/tls13_enc.c (22a7fd65ca59aa889ed516b87e9c752abc44f006)
++++ contrib/libs/openssl/ssl/tls13_enc.c (a1710ceeba972d163108e8a86bba74b090c8f4c8)
+@@ -435,8 +435,6 @@ static int derive_secret_key_and_iv(SSL *s, int sending, const EVP_MD *md,
+ return 0;
+ }
+
+-int tls13_change_cipher_state(SSL *s, int which)
+-{
+ #ifdef CHARSET_EBCDIC
+ static const unsigned char client_early_traffic[] = {0x63, 0x20, 0x65, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00};
+ static const unsigned char client_handshake_traffic[] = {0x63, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00};
+@@ -456,6 +454,217 @@ int tls13_change_cipher_state(SSL *s, int which)
+ static const unsigned char resumption_master_secret[] = "res master";
+ static const unsigned char early_exporter_master_secret[] = "e exp master";
+ #endif
++
++#ifndef OPENSSL_NO_QUIC
++static int quic_change_cipher_state(SSL *s, int which)
++{
++ unsigned char hash[EVP_MAX_MD_SIZE];
++ size_t hashlen = 0;
++ int hashleni;
++ int ret = 0;
++ const EVP_MD *md = NULL;
++ OSSL_ENCRYPTION_LEVEL level;
++ int is_handshake = ((which & SSL3_CC_HANDSHAKE) == SSL3_CC_HANDSHAKE);
++ int is_client_read = ((which & SSL3_CHANGE_CIPHER_CLIENT_READ) == SSL3_CHANGE_CIPHER_CLIENT_READ);
++ int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE);
++ int is_early = (which & SSL3_CC_EARLY);
++
++ if (is_early) {
++ EVP_MD_CTX *mdctx = NULL;
++ long handlen;
++ void *hdata;
++ unsigned int hashlenui;
++ const SSL_CIPHER *sslcipher = SSL_SESSION_get0_cipher(s->session);
++
++ handlen = BIO_get_mem_data(s->s3->handshake_buffer, &hdata);
++ if (handlen <= 0) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
++ SSL_R_BAD_HANDSHAKE_LENGTH);
++ goto err;
++ }
++
++ if (s->early_data_state == SSL_EARLY_DATA_CONNECTING
++ && s->max_early_data > 0 && s->session->ext.max_early_data == 0) {
++ /*
++ * If we are attempting to send early data, and we've decided to
++ * actually do it but max_early_data in s->session is 0 then we
++ * must be using an external PSK.
++ */
++ if (!ossl_assert(s->psksession != NULL
++ && s->max_early_data
++ == s->psksession->ext.max_early_data)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
++ SSL_F_QUIC_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
++ goto err;
++ }
++ sslcipher = SSL_SESSION_get0_cipher(s->psksession);
++ }
++ if (sslcipher == NULL) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
++ SSL_R_BAD_PSK);
++ goto err;
++ }
++
++ /*
++ * We need to calculate the handshake digest using the digest from
++ * the session. We haven't yet selected our ciphersuite so we can't
++ * use ssl_handshake_md().
++ */
++ mdctx = EVP_MD_CTX_new();
++ if (mdctx == NULL) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
++ ERR_R_MALLOC_FAILURE);
++ goto err;
++ }
++ md = ssl_md(sslcipher->algorithm2);
++ if (md == NULL || !EVP_DigestInit_ex(mdctx, md, NULL)
++ || !EVP_DigestUpdate(mdctx, hdata, handlen)
++ || !EVP_DigestFinal_ex(mdctx, hash, &hashlenui)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
++ ERR_R_INTERNAL_ERROR);
++ EVP_MD_CTX_free(mdctx);
++ goto err;
++ }
++ hashlen = hashlenui;
++ EVP_MD_CTX_free(mdctx);
++ } else {
++ md = ssl_handshake_md(s);
++ if (!ssl3_digest_cached_records(s, 1)
++ || !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) {
++ /* SSLfatal() already called */;
++ goto err;
++ }
++
++ /* Ensure cast to size_t is safe */
++ hashleni = EVP_MD_size(md);
++ if (!ossl_assert(hashleni >= 0)) {
++ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE,
++ ERR_R_EVP_LIB);
++ goto err;
++ }
++ hashlen = (size_t)hashleni;
++ }
++
++ if (is_client_read || is_server_write) {
++ if (is_handshake) {
++ /*
++ * This looks a bit weird, since the condition is basically "the
++ * server is writing" but we set both the server *and* client
++ * handshake traffic keys here. That's because there's only a fixed
++ * number of change-cipher-state events in the TLS 1.3 handshake,
++ * and in particular there's not an event in between when the server
++ * writes encrypted handshake messages and when the client writes
++ * encrypted handshake messages, so we generate both here.
++ */
++ level = ssl_encryption_handshake;
++
++ if (!tls13_hkdf_expand(s, md, s->handshake_secret,
++ client_handshake_traffic,
++ sizeof(client_handshake_traffic)-1, hash,
++ hashlen, s->client_hand_traffic_secret,
++ hashlen, 1)
++ || !ssl_log_secret(s, CLIENT_HANDSHAKE_LABEL,
++ s->client_hand_traffic_secret, hashlen)
++ || !tls13_derive_finishedkey(s, md,
++ s->client_hand_traffic_secret,
++ s->client_finished_secret, hashlen)
++ || !tls13_hkdf_expand(s, md, s->handshake_secret,
++ server_handshake_traffic,
++ sizeof(server_handshake_traffic)-1, hash,
++ hashlen, s->server_hand_traffic_secret,
++ hashlen, 1)
++ || !ssl_log_secret(s, SERVER_HANDSHAKE_LABEL,
++ s->server_hand_traffic_secret, hashlen)
++ || !tls13_derive_finishedkey(s, md,
++ s->server_hand_traffic_secret,
++ s->server_finished_secret,
++ hashlen)) {
++ /* SSLfatal() already called */
++ goto err;
++ }
++ } else {
++ /*
++ * As above, we generate both sets of application traffic keys at
++ * the same time.
++ */
++ level = ssl_encryption_application;
++
++ if (!tls13_hkdf_expand(s, md, s->master_secret,
++ client_application_traffic,
++ sizeof(client_application_traffic)-1, hash,
++ hashlen, s->client_app_traffic_secret,
++ hashlen, 1)
++ || !ssl_log_secret(s, CLIENT_APPLICATION_LABEL,
++ s->client_app_traffic_secret, hashlen)
++ || !tls13_hkdf_expand(s, md, s->master_secret,
++ server_application_traffic,
++ sizeof(server_application_traffic)-1,
++ hash, hashlen,
++ s->server_app_traffic_secret, hashlen, 1)
++ || !ssl_log_secret(s, SERVER_APPLICATION_LABEL,
++ s->server_app_traffic_secret, hashlen)) {
++ /* SSLfatal() already called */
++ goto err;
++ }
++ }
++ if (!quic_set_encryption_secrets(s, level)) {
++ /* SSLfatal() already called */
++ goto err;
++ }
++ if (s->server)
++ s->quic_write_level = level;
++ else
++ s->quic_read_level = level;
++ } else {
++ /* is_client_write || is_server_read */
++
++ if (is_early) {
++ level = ssl_encryption_early_data;
++
++ if (!tls13_hkdf_expand(s, md, s->early_secret, client_early_traffic,
++ sizeof(client_early_traffic)-1, hash,
++ hashlen, s->client_early_traffic_secret,
++ hashlen, 1)
++ || !ssl_log_secret(s, CLIENT_EARLY_LABEL,
++ s->client_early_traffic_secret, hashlen)
++ || !quic_set_encryption_secrets(s, level)) {
++ /* SSLfatal() already called */
++ goto err;
++ }
++ } else if (is_handshake) {
++ level = ssl_encryption_handshake;
++ } else {
++ level = ssl_encryption_application;
++ /*
++ * We also create the resumption master secret, but this time use the
++ * hash for the whole handshake including the Client Finished
++ */
++ if (!tls13_hkdf_expand(s, md, s->master_secret,
++ resumption_master_secret,
++ sizeof(resumption_master_secret)-1, hash,
++ hashlen, s->resumption_master_secret,
++ hashlen, 1)) {
++ /* SSLfatal() already called */
++ goto err;
++ }
++ }
++
++ if (level != ssl_encryption_early_data) {
++ if (s->server)
++ s->quic_read_level = level;
++ else
++ s->quic_write_level = level;
++ }
++ }
++
++ ret = 1;
++ err:
++ return ret;
++}
++#endif /* OPENSSL_NO_QUIC */
++
++int tls13_change_cipher_state(SSL *s, int which)
++{
+ unsigned char *iv;
+ unsigned char secret[EVP_MAX_MD_SIZE];
+ unsigned char hashval[EVP_MAX_MD_SIZE];
+@@ -471,6 +680,11 @@ int tls13_change_cipher_state(SSL *s, int which)
+ const EVP_MD *md = NULL;
+ const EVP_CIPHER *cipher = NULL;
+
++#ifndef OPENSSL_NO_QUIC
++ if (SSL_IS_QUIC(s))
++ return quic_change_cipher_state(s, which);
++#endif
++
+ if (which & SSL3_CC_READ) {
+ if (s->enc_read_ctx != NULL) {
+ EVP_CIPHER_CTX_reset(s->enc_read_ctx);
diff --git a/ya b/ya
index 06f15bea382..d9af68c5939 100755
--- a/ya
+++ b/ya
@@ -46,33 +46,33 @@ REGISTRY_ENDPOINT = os.environ.get("YA_REGISTRY_ENDPOINT", "https://devtools-reg
PLATFORM_MAP = {
"data": {
"win32": {
- "md5": "5993d61d146feed1604c382bed2e73c0",
+ "md5": "0080c0947dc0ddf9b20ca50821b32356",
"urls": [
- f"{REGISTRY_ENDPOINT}/12765041121"
+ f"{REGISTRY_ENDPOINT}/12778691402"
]
},
"darwin": {
- "md5": "9595fda8348764a35551cfbad27f7e78",
+ "md5": "257864a1bc4ab2f0beef69bb19c5944f",
"urls": [
- f"{REGISTRY_ENDPOINT}/12765039891"
+ f"{REGISTRY_ENDPOINT}/12778688709"
]
},
"darwin-arm64": {
- "md5": "4e9c49cac8c80522d905d18ece28f272",
+ "md5": "8166643048c1c44d3b172d57f9f558b4",
"urls": [
- f"{REGISTRY_ENDPOINT}/12765038014"
+ f"{REGISTRY_ENDPOINT}/12778685977"
]
},
"linux-aarch64": {
- "md5": "c19d06ae8b572e2bbd42eca68948a120",
+ "md5": "0cb28f3d593d8e7751db41688da47d48",
"urls": [
- f"{REGISTRY_ENDPOINT}/12765037033"
+ f"{REGISTRY_ENDPOINT}/12778683580"
]
},
"linux": {
- "md5": "147dca14524512342979bc4951ef7623",
+ "md5": "eada2433c6c25c0096f9c77433a82fdf",
"urls": [
- f"{REGISTRY_ENDPOINT}/12765042559"
+ f"{REGISTRY_ENDPOINT}/12778694423"
]
}
}
diff --git a/yql/essentials/cfg/tests/gateways-experimental.conf b/yql/essentials/cfg/tests/gateways-experimental.conf
index f2902fdeebe..fddec6af53a 100644
--- a/yql/essentials/cfg/tests/gateways-experimental.conf
+++ b/yql/essentials/cfg/tests/gateways-experimental.conf
@@ -105,11 +105,6 @@ Yt {
}
DefaultSettings {
- Name: "UseNativeDescSort"
- Value: "true"
- }
-
- DefaultSettings {
Name: "QueryCacheCombineChunksReplace"
Value: "true"
}
diff --git a/yql/essentials/core/common_opt/yql_co_flow2.cpp b/yql/essentials/core/common_opt/yql_co_flow2.cpp
index b0db937ba12..e9dc9b1a617 100644
--- a/yql/essentials/core/common_opt/yql_co_flow2.cpp
+++ b/yql/essentials/core/common_opt/yql_co_flow2.cpp
@@ -1259,11 +1259,11 @@ TExprNode::TPtr PullUpFlatMapOverEquiJoin(const TExprNode::TPtr& node, TExprCont
YQL_ENSURE(status == IGraphTransformer::TStatus::Ok);
status = EquiJoinAnnotation(node->Pos(), canaryResultType, canaryLabels,
- *joinTreeWithInputRenames, options, ctx);
+ *joinTreeWithInputRenames, options, ctx, *optCtx.Types);
YQL_ENSURE(status == IGraphTransformer::TStatus::Ok);
status = EquiJoinAnnotation(node->Pos(), noRenamesResultType, actualLabels,
- *joinTree, options, ctx);
+ *joinTree, options, ctx, *optCtx.Types);
YQL_ENSURE(status == IGraphTransformer::TStatus::Ok);
}
diff --git a/yql/essentials/core/common_opt/yql_co_transformer.cpp b/yql/essentials/core/common_opt/yql_co_transformer.cpp
index 7f3b83f7b08..221fe7a7959 100644
--- a/yql/essentials/core/common_opt/yql_co_transformer.cpp
+++ b/yql/essentials/core/common_opt/yql_co_transformer.cpp
@@ -7,6 +7,7 @@
#include <yql/essentials/core/yql_join.h>
#include <yql/essentials/core/yql_opt_utils.h>
#include <yql/essentials/core/yql_opt_window.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/utils/log/log.h>
#include <yql/essentials/utils/yql_panic.h>
@@ -94,7 +95,7 @@ IGraphTransformer::TStatus TCommonOptTransformer::DoTransform(TExprNode::TPtr in
return status;
}
- if (TypeCtx_->LangVer >= MakeLangVersion(2025, 4)) {
+ if (TypeCtx_->LangVer >= NFeature::LinearTypes.MinLangVer) {
status = OptimizeBlocks(input = std::move(output), output, ctx, *TypeCtx_);
if (status.Level != IGraphTransformer::TStatus::Ok) {
return status;
diff --git a/yql/essentials/core/facade/yql_facade.cpp b/yql/essentials/core/facade/yql_facade.cpp
index 93f64023586..60595a52c1a 100644
--- a/yql/essentials/core/facade/yql_facade.cpp
+++ b/yql/essentials/core/facade/yql_facade.cpp
@@ -214,6 +214,10 @@ void TProgramFactory::EnableRangeComputeFor() {
EnableRangeComputeFor_ = true;
}
+void TProgramFactory::EnableAutoUseYqlLibs() {
+ AutoUseYqlLibs_ = true;
+}
+
void TProgramFactory::SetIssueReportTarget(const TString& reportTarget) {
IssueReportTarget_ = reportTarget;
}
@@ -317,7 +321,7 @@ TProgramPtr TProgramFactory::Create(
return new TProgram(IssueReportTarget_, FunctionRegistry_, randomProvider, timeProvider, NextUniqueId_, DataProvidersInit_,
LangVer_, MaxLangVer_, VolatileResults_, UserDataTable_, Credentials_, moduleResolver, urlListerManager,
udfResolver, udfIndex, udfIndexPackageSet, FileStorage_, UrlPreprocessing_,
- GatewaysConfig_, filename, sourceCode, sessionId, Runner_, EnableRangeComputeFor_, ArrowResolver_, hiddenMode,
+ GatewaysConfig_, filename, sourceCode, sessionId, Runner_, EnableRangeComputeFor_, AutoUseYqlLibs_, ArrowResolver_, hiddenMode,
qContext, RemoteLayersProviders_);
}
@@ -349,6 +353,7 @@ TProgram::TProgram(
TString sessionId,
const TString& runner,
bool enableRangeComputeFor,
+ bool autoUseYqlLibs,
IArrowResolver::TPtr arrowResolver,
EHiddenMode hiddenMode,
const TQContext& qContext,
@@ -441,6 +446,12 @@ TProgram::TProgram(
}
}
+ for (auto& [key, block] : SavedUserDataTable_) {
+ if (autoUseYqlLibs && key.Alias().StartsWith(NYql::GetDefaultFilePrefix() + "yql_libs/")) {
+ block.Usage.Set(EUserDataBlockUsage::Library, /*val=*/true); // See YQL-21401
+ }
+ }
+
UserDataStorage_ = MakeIntrusive<TUserDataStorage>(fileStorage, SavedUserDataTable_, udfResolver, udfIndex);
if (auto modules = dynamic_cast<TModuleResolver*>(Modules_.get())) {
modules->AttachUserData(UserDataStorage_);
diff --git a/yql/essentials/core/facade/yql_facade.h b/yql/essentials/core/facade/yql_facade.h
index f4c0a4e1049..80635a8e9d4 100644
--- a/yql/essentials/core/facade/yql_facade.h
+++ b/yql/essentials/core/facade/yql_facade.h
@@ -64,6 +64,7 @@ public:
void SetFileStorage(TFileStoragePtr fileStorage);
void SetUrlPreprocessing(IUrlPreprocessing::TPtr urlPreprocessing);
void EnableRangeComputeFor();
+ void EnableAutoUseYqlLibs();
void SetArrowResolver(IArrowResolver::TPtr arrowResolver);
void SetUdfResolverLogfile(const TString& path);
void AddRemoteLayersProvider(const TString& alias, NLayers::IRemoteLayerProviderPtr provider);
@@ -104,6 +105,7 @@ private:
IUrlPreprocessing::TPtr UrlPreprocessing_;
TString Runner_;
bool EnableRangeComputeFor_ = false;
+ bool AutoUseYqlLibs_ = false;
IArrowResolver::TPtr ArrowResolver_;
TMaybe<TString> UdfResolverLogfile_;
THashMap<TString, NLayers::IRemoteLayerProviderPtr> RemoteLayersProviders_;
@@ -403,6 +405,7 @@ private:
TString sessionId,
const TString& runner,
bool enableRangeComputeFor,
+ bool autoUseYqlLibs,
IArrowResolver::TPtr arrowResolver,
EHiddenMode hiddenMode,
const TQContext& qContext,
diff --git a/yql/essentials/core/langver/feature.cpp b/yql/essentials/core/langver/feature.cpp
index c57315d627a..db9b7659e8b 100644
--- a/yql/essentials/core/langver/feature.cpp
+++ b/yql/essentials/core/langver/feature.cpp
@@ -1,5 +1,7 @@
#include "feature.h"
+#include <yql/essentials/utils/yql_panic.h>
+
#include <util/string/builder.h>
namespace NYql {
@@ -43,6 +45,16 @@ std::expected<std::monostate, TError> GetAvailability(
} // namespace
+TFeature TFeature::Finish() && {
+ YQL_ENSURE(
+ MinLangVer == UnknownLangVersion || IsValidLangVersion(MinLangVer),
+ "LangVer: " << FormatLangVersion(MinLangVer));
+ YQL_ENSURE(
+ MaxLangVer == UnknownLangVersion || IsValidLangVersion(MaxLangVer),
+ "LangVer: " << FormatLangVersion(MaxLangVer));
+ return *this;
+}
+
bool IsAvailableOn(
TLangVersion current,
EBackportCompatibleFeaturesMode mode,
diff --git a/yql/essentials/core/langver/feature.gen.py b/yql/essentials/core/langver/feature.gen.py
index 968e52e4cac..4f1fa5ba585 100644
--- a/yql/essentials/core/langver/feature.gen.py
+++ b/yql/essentials/core/langver/feature.gen.py
@@ -54,12 +54,12 @@ def emit_feature(name: str, attrs: dict[str, Any]) -> Generator[str]:
min_langver = parse_langver(attrs.get("min_langver", "unknown"))
max_langver = parse_langver(attrs.get("max_langver", "unknown"))
- yield f'const TFeature {name} = {{'
+ yield f'const TFeature {name} = (TFeature{{'
yield f' .Name = "{name}",'
yield f' .Description = "{description}",'
yield f' .MinLangVer = {min_langver},'
yield f' .MaxLangVer = {max_langver},'
- yield f'}};'
+ yield f'}}).Finish();'
yield ""
diff --git a/yql/essentials/core/langver/feature.h b/yql/essentials/core/langver/feature.h
index 3f242821c71..48a6c9d5ad1 100644
--- a/yql/essentials/core/langver/feature.h
+++ b/yql/essentials/core/langver/feature.h
@@ -11,6 +11,8 @@ struct TFeature {
TString Description;
TLangVersion MinLangVer = UnknownLangVersion;
TLangVersion MaxLangVer = UnknownLangVersion;
+
+ TFeature Finish() &&;
};
bool IsAvailableOn(
diff --git a/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp b/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp
index 1b74ca1e817..e3878dc97f3 100644
--- a/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp
+++ b/yql/essentials/core/peephole_opt/yql_opt_peephole_physical.cpp
@@ -652,7 +652,7 @@ void GetKeys(const TJoinLabels& joinLabels, const TExprNode& keys, TExprContext&
}
}
-TExprNode::TPtr ExpandEquiJoinImpl(const TExprNode& node, TExprContext& ctx) {
+TExprNode::TPtr ExpandEquiJoinImpl(const TExprNode& node, TExprContext& ctx, const TTypeAnnotationContext& types) {
if (node.ChildrenSize() > 4U) {
return SplitEquiJoinToPairs(node, ctx);
}
@@ -743,7 +743,7 @@ TExprNode::TPtr ExpandEquiJoinImpl(const TExprNode& node, TExprContext& ctx) {
} else if (rightKind){
keyTypeItems.emplace_back(JoinDryKeyType(keyType2, keyType1, optKey, ctx));
} else {
- keyTypeItems.emplace_back(CommonType<true>(node.Pos(), DryType(keyType1, optKey, ctx), DryType(keyType2, optKey, ctx), ctx));
+ keyTypeItems.emplace_back(CommonType<true>(node.Pos(), DryType(keyType1, optKey, ctx), DryType(keyType2, optKey, ctx), ctx, types));
optKey = optKey && !filter;
}
badKey = !keyTypeItems.back();
@@ -987,9 +987,9 @@ TExprNode::TPtr PeepHoleDictFromKeysToDict(const TExprNode::TPtr& node, TExprCon
.Ptr();
}
-TExprNode::TPtr ExpandEquiJoin(const TExprNode::TPtr& input, TExprContext& ctx) {
+TExprNode::TPtr ExpandEquiJoin(const TExprNode::TPtr& input, TExprContext& ctx, TTypeAnnotationContext& types) {
YQL_ENSURE(input->ChildrenSize() >= 4);
- return ExpandEquiJoinImpl(*input, ctx);
+ return ExpandEquiJoinImpl(*input, ctx, types);
}
template <bool Strong>
@@ -2079,7 +2079,7 @@ TExprNode::TPtr BuildDictOverList(TPositionHandle pos, const TExprNode::TPtr& co
.Build();
}
-TExprNode::TPtr BuildDictOverTuple(TExprNode::TPtr&& collection, const TTypeAnnotationNode*& dictKeyType, TExprContext& ctx)
+TExprNode::TPtr BuildDictOverTuple(TExprNode::TPtr&& collection, const TTypeAnnotationNode*& dictKeyType, TExprContext& ctx, const TTypeAnnotationContext& typesCtx)
{
const auto pos = collection->Pos();
const auto tupleType = collection->GetTypeAnn()->Cast<TTupleExprType>();
@@ -2088,7 +2088,7 @@ TExprNode::TPtr BuildDictOverTuple(TExprNode::TPtr&& collection, const TTypeAnno
return nullptr;
}
TTypeAnnotationNode::TListType types(tupleType->GetItems());
- dictKeyType = CommonType(pos, types, ctx);
+ dictKeyType = CommonType(pos, types, ctx, typesCtx);
YQL_ENSURE(dictKeyType, "Uncompatible collection elements.");
TExprNode::TPtr tuple;
@@ -2111,7 +2111,7 @@ TExprNode::TPtr BuildDictOverTuple(TExprNode::TPtr&& collection, const TTypeAnno
return ctx.NewCallable(pos, "DictFromKeys", {ExpandType(pos, *dictKeyType, ctx), std::move(tuple)});
}
-TExprNode::TPtr ExpandSqlIn(const TExprNode::TPtr& input, TExprContext& ctx) {
+TExprNode::TPtr ExpandSqlIn(const TExprNode::TPtr& input, TExprContext& ctx, TTypeAnnotationContext& types) {
auto collection = input->HeadPtr();
auto lookup = input->ChildPtr(1);
auto options = input->ChildPtr(2);
@@ -2160,7 +2160,7 @@ TExprNode::TPtr ExpandSqlIn(const TExprNode::TPtr& input, TExprContext& ctx) {
}
} else if (collectionType->GetKind() == ETypeAnnotationKind::Tuple) {
YQL_CLOG(DEBUG, CorePeepHole) << "IN Tuple";
- dict = BuildDictOverTuple(std::move(collection), dictKeyType, ctx);
+ dict = BuildDictOverTuple(std::move(collection), dictKeyType, ctx, types);
} else if (collectionType->GetKind() == ETypeAnnotationKind::EmptyDict) {
YQL_CLOG(DEBUG, CorePeepHole) << "IN EmptyDict";
} else if (collectionType->GetKind() == ETypeAnnotationKind::EmptyList) {
@@ -9348,11 +9348,9 @@ ui64 ToTimestamp(ui64 now) { return std::min<ui64>(NUdf::MAX_TIMESTAMP - 1ULL, n
struct TPeepHoleRules {
const TPeepHoleOptimizerMap CommonStageRules = {
- {"EquiJoin", &ExpandEquiJoin},
{"SafeCast", &ExpandCast<false>},
{"StrictCast", &ExpandCast<true>},
{"AlterTo", &ExpandAlterTo},
- {"SqlIn", &ExpandSqlIn},
{"Lookup", &RewriteSearchByKeyForTypesMismatch<false>},
{"Contains", &RewriteSearchByKeyForTypesMismatch<true>},
{"ListHas", &ExpandListHas},
@@ -9434,6 +9432,8 @@ struct TPeepHoleRules {
};
const TExtPeepHoleOptimizerMap CommonStageExtRules = {
+ {"EquiJoin", &ExpandEquiJoin},
+ {"SqlIn", &ExpandSqlIn},
{"Aggregate", &ExpandAggregatePeephole},
{"AggregateCombine", &ExpandAggregatePeephole},
{"AggregateCombineState", &ExpandAggregatePeephole},
diff --git a/yql/essentials/core/type_ann/type_ann_core.cpp b/yql/essentials/core/type_ann/type_ann_core.cpp
index 74dca3df57a..399e1254315 100644
--- a/yql/essentials/core/type_ann/type_ann_core.cpp
+++ b/yql/essentials/core/type_ann/type_ann_core.cpp
@@ -19,6 +19,7 @@
#include <yql/essentials/core/yql_type_helpers.h>
#include <yql/essentials/public/issue/protos/issue_id.pb.h>
#include <yql/essentials/core/issue/yql_issue.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/core/expr_nodes_gen/yql_expr_nodes_gen.h>
#include <yql/essentials/core/yql_window_features.h>
#include <yql/essentials/minikql/dom/json.h>
@@ -2068,7 +2069,7 @@ namespace NTypeAnnImpl {
flattenItemType = ctx.Expr.MakeType<TTupleExprType>(TTypeAnnotationNode::TListType({keyType, payloadType}));
allFieldOptional = false;
} else if (mode == "optional" || (mode == "auto" && fieldOptional)) {
- if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, MakeLangVersion(2025, 03), ctx.Types.BackportMode) && !fieldOptional) {
+ if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, NFeature::FlattenOptionalByNonOptional.MinLangVer, ctx.Types.BackportMode) && !fieldOptional) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(structObj->Pos()), TStringBuilder() <<
"Expected optional type in field of struct: '" << fieldName <<
"', but got: " << *field->GetItemType()));
@@ -2644,7 +2645,7 @@ namespace NTypeAnnImpl {
}
}
- const auto commonItemType = CommonTypeForChildren(*input, ctx.Expr);
+ const auto commonItemType = CommonTypeForChildren(*input, ctx.Expr, ctx.Types);
if (!commonItemType) {
return IGraphTransformer::TStatus::Error;
}
@@ -3872,7 +3873,7 @@ namespace NTypeAnnImpl {
template<bool IsScore>
IGraphTransformer::TStatus FullTextBuiltinWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
YQL_ENSURE(output);
- if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, MakeLangVersion(2025, 05), ctx.Types.BackportMode)) {
+ if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, NFeature::FullTextFunction.MinLangVer, ctx.Types.BackportMode)) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), TStringBuilder() << input->Content() << " function is not available before version 2025.05"));
return IGraphTransformer::TStatus::Error;
}
@@ -3943,7 +3944,7 @@ namespace NTypeAnnImpl {
IGraphTransformer::TStatus HybridRankBuiltinWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
YQL_ENSURE(output);
- if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, MakeLangVersion(2026, 02), ctx.Types.BackportMode)) {
+ if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, NFeature::HybridRankFunction.MinLangVer, ctx.Types.BackportMode)) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), "HybridRank function is not available before version 2026.02"));
return IGraphTransformer::TStatus::Error;
}
@@ -4036,7 +4037,7 @@ namespace NTypeAnnImpl {
}
IGraphTransformer::TStatus SqlConcatWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
- if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, MakeLangVersion(2025, 04), ctx.Types.BackportMode)) {
+ if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, NFeature::ConcatFunction.MinLangVer, ctx.Types.BackportMode)) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), "Concat function is not available before version 2025.04"));
return IGraphTransformer::TStatus::Error;
}
@@ -4085,7 +4086,7 @@ namespace NTypeAnnImpl {
}
IGraphTransformer::TStatus NullIfWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
- if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, MakeLangVersion(2025, 04), ctx.Types.BackportMode)) {
+ if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, NFeature::NullIfFunction.MinLangVer, ctx.Types.BackportMode)) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), "NullIf function is not available before version 2025.04"));
return IGraphTransformer::TStatus::Error;
}
@@ -4385,7 +4386,7 @@ namespace NTypeAnnImpl {
output = ctx.Expr.RenameNode(*input, "AsList");
return IGraphTransformer::TStatus::Repeat;
- } else if (const auto commonItemType = CommonTypeForChildren(*input, ctx.Expr, warn)) {
+ } else if (const auto commonItemType = CommonTypeForChildren(*input, ctx.Expr, ctx.Types, warn)) {
if (const auto status = ConvertChildrenToType(input, commonItemType, ctx.Expr, ctx.Types);
status != IGraphTransformer::TStatus::Ok) {
return status;
@@ -7501,7 +7502,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
output = ctx.Expr.RenameNode(*input, IsSet ? "AsSet" : "AsDict");
return IGraphTransformer::TStatus::Repeat;
- } else if (const auto commonType = CommonTypeForChildren(*input, ctx.Expr, warn)) {
+ } else if (const auto commonType = CommonTypeForChildren(*input, ctx.Expr, ctx.Types, warn)) {
if (const auto status = ConvertChildrenToType(input, commonType, ctx.Expr, ctx.Types);
status != IGraphTransformer::TStatus::Ok) {
return status;
@@ -7613,7 +7614,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
*thenType << ", else type: " << *elseType));
return IGraphTransformer::TStatus::Error;
}
- } else if (const auto commonType = CommonType<false>(input->Pos(), thenType, elseType, ctx.Expr)) {
+ } else if (const auto commonType = CommonType<false>(input->Pos(), thenType, elseType, ctx.Expr, ctx.Types)) {
if (const auto status = TryConvertTo(input->ChildRef(1), *commonType, ctx.Expr, ctx.Types)
.Combine(TryConvertTo(input->TailRef(), *commonType, ctx.Expr, ctx.Types));
status != IGraphTransformer::TStatus::Ok)
@@ -10588,7 +10589,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
IGraphTransformer::TStatus BuildSimplePgCall(TPositionHandle pos, TStringBuf name,
const TExprNodeList& args, TExprNode::TPtr& output, TExtContext& ctx) {
- if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, MakeLangVersion(2025, 04), ctx.Types.BackportMode)) {
+ if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, NFeature::SimplePgFunction.MinLangVer, ctx.Types.BackportMode)) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(pos), "SimplePg functions are not available before version 2025.04"));
return IGraphTransformer::TStatus::Error;
}
@@ -13702,7 +13703,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
IGraphTransformer::TStatus WithSideEffectsModeWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
Y_UNUSED(output);
- if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, MakeLangVersion(2025, 04), ctx.Types.BackportMode)) {
+ if (!IsBackwardCompatibleFeatureAvailable(ctx.Types.LangVer, NFeature::SideEffects.MinLangVer, ctx.Types.BackportMode)) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), "SideEffects is not available before version 2025.04"));
return IGraphTransformer::TStatus::Error;
}
@@ -15742,7 +15743,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
}
IGraphTransformer::TStatus LinearDestroyWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
- if (!IsAvailableLangVersion(MakeLangVersion(2025, 5), ctx.Types.LangVer)) {
+ if (!IsAvailableLangVersion(NFeature::LinearDestroy.MinLangVer, ctx.Types.LangVer)) {
ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), "LinearDestroy is not available before version 2025.05"));
return IGraphTransformer::TStatus::Error;
}
@@ -16029,13 +16030,13 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
Functions["Merge"] = &ExtendWrapper;
Functions["Extract"] = &ExtractWrapper;
Functions["OrderedExtract"] = &ExtractWrapper;
- Functions["UnionAll"] = &SelectOpWrapper;
- Functions["UnionMerge"] = &SelectOpWrapper;
- Functions["Union"] = &SelectOpWrapper;
- Functions["IntersectAll"] = &SelectOpWrapper;
- Functions["Intersect"] = &SelectOpWrapper;
- Functions["ExceptAll"] = &SelectOpWrapper;
- Functions["Except"] = &SelectOpWrapper;
+ ExtFunctions["UnionAll"] = &SelectOpWrapper;
+ ExtFunctions["UnionMerge"] = &SelectOpWrapper;
+ ExtFunctions["Union"] = &SelectOpWrapper;
+ ExtFunctions["IntersectAll"] = &SelectOpWrapper;
+ ExtFunctions["Intersect"] = &SelectOpWrapper;
+ ExtFunctions["ExceptAll"] = &SelectOpWrapper;
+ ExtFunctions["Except"] = &SelectOpWrapper;
ExtFunctions["ListExtend"] = &ListExtendWrapper<false>;
ExtFunctions["ListExtendStrict"] = &ListExtendWrapper<true>;
Functions["ListUnionAll"] = &ListUnionAllWrapper;
@@ -16227,7 +16228,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
Functions["ListJoinCore"] = &ListJoinCoreWrapper;
Functions["CombineCore"] = &CombineCoreWrapper;
Functions["GroupingCore"] = &GroupingCoreWrapper;
- Functions["EquiJoin"] = &EquiJoinWrapper;
+ ExtFunctions["EquiJoin"] = &EquiJoinWrapper;
Functions["OptionalReduce"] = &OptionalReduceWrapper;
ExtFunctions["OptionalItemType"] = &TypeArgWrapper<ETypeArgument::OptionalItem>;
ExtFunctions["LinearItemType"] = &TypeArgWrapper<ETypeArgument::LinearItem>;
@@ -16303,7 +16304,7 @@ template <NKikimr::NUdf::EDataSlot DataSlot>
Functions["SqlAccess"] = &SqlAccessWrapper;
Functions["SqlProcess"] = &SqlProcessWrapper;
Functions["SqlReduce"] = &SqlReduceWrapper;
- Functions["SqlCombine"] = &SqlCombineWrapper;
+ ExtFunctions["SqlCombine"] = &SqlCombineWrapper;
Functions["SqlExternalFunction"] = &SqlExternalFunctionWrapper;
Functions["SqlExtractKey"] = &SqlExtractKeyWrapper;
Functions["SqlReduceUdf"] = &SqlReduceUdfWrapper;
diff --git a/yql/essentials/core/type_ann/type_ann_impl.h b/yql/essentials/core/type_ann/type_ann_impl.h
index 0794e17d1d1..0c63b22dcf1 100644
--- a/yql/essentials/core/type_ann/type_ann_impl.h
+++ b/yql/essentials/core/type_ann/type_ann_impl.h
@@ -34,7 +34,7 @@ namespace NYql::NTypeAnnImpl {
IGraphTransformer::TStatus GraceJoinCoreWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus GraceSelfJoinCoreWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus CommonJoinCoreWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
- IGraphTransformer::TStatus EquiJoinWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
+ IGraphTransformer::TStatus EquiJoinWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx);
IGraphTransformer::TStatus CombineCoreWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus GroupingCoreWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus DecimalBinaryWrapperBase(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx, bool blocks);
diff --git a/yql/essentials/core/type_ann/type_ann_join.cpp b/yql/essentials/core/type_ann/type_ann_join.cpp
index 9cac5d130f9..8e353e7106c 100644
--- a/yql/essentials/core/type_ann/type_ann_join.cpp
+++ b/yql/essentials/core/type_ann/type_ann_join.cpp
@@ -274,7 +274,7 @@ namespace NYql::NTypeAnnImpl {
return IGraphTransformer::TStatus::Ok;
}
- IGraphTransformer::TStatus EquiJoinWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
+ IGraphTransformer::TStatus EquiJoinWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
if (!EnsureMinArgsCount(*input, 4, ctx.Expr)) {
return IGraphTransformer::TStatus::Error;
}
@@ -352,7 +352,7 @@ namespace NYql::NTypeAnnImpl {
}
const TStructExprType* resultType = nullptr;
- status = EquiJoinAnnotation(input->Pos(), resultType, labels, *joins, options, ctx.Expr);
+ status = EquiJoinAnnotation(input->Pos(), resultType, labels, *joins, options, ctx.Expr, ctx.Types);
if (status != IGraphTransformer::TStatus::Ok) {
return status;
}
diff --git a/yql/essentials/core/type_ann/type_ann_list.cpp b/yql/essentials/core/type_ann/type_ann_list.cpp
index d2cd11fad47..acc3178eeb9 100644
--- a/yql/essentials/core/type_ann/type_ann_list.cpp
+++ b/yql/essentials/core/type_ann/type_ann_list.cpp
@@ -2663,7 +2663,7 @@ namespace {
input->SetTypeAnn(ctx.Expr.MakeType<TUniversalExprType>());
return IGraphTransformer::TStatus::Ok;
}
- auto commonType = CommonType<false>(input->Pos(), input->Child(idx1)->GetTypeAnn(), input->Child(idx2)->GetTypeAnn(), ctx.Expr);
+ auto commonType = CommonType<false>(input->Pos(), input->Child(idx1)->GetTypeAnn(), input->Child(idx2)->GetTypeAnn(), ctx.Expr, ctx.Types);
if (!commonType)
return IGraphTransformer::TStatus::Error;
if (ETypeAnnotationKind::Optional == commonType->GetKind()) {
@@ -2729,7 +2729,7 @@ namespace {
return status;
}
} else {
- commonType = CommonType<false>(input->Pos(), input->Child(0U)->GetTypeAnn(), input->Child(1U)->GetTypeAnn(), ctx.Expr);
+ commonType = CommonType<false>(input->Pos(), input->Child(0U)->GetTypeAnn(), input->Child(1U)->GetTypeAnn(), ctx.Expr, ctx.Types);
if (!commonType)
return IGraphTransformer::TStatus::Error;
@@ -3164,7 +3164,7 @@ namespace {
return IGraphTransformer::TStatus::Ok;
}
- IGraphTransformer::TStatus SelectOpWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
+ IGraphTransformer::TStatus SelectOpWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
const bool checkHashes = !input->IsCallable("UnionAll") && !input->IsCallable("UnionMerge");
switch (input->ChildrenSize()) {
case 0U:
@@ -3253,7 +3253,7 @@ namespace {
TPositionHandle pos,
const TExprNode::TListType& children,
const TStructExprType*& resultStructType,
- TContext& ctx,
+ TExtContext& ctx,
const bool areHashesChecked,
bool& isUniversal)
{
@@ -3292,7 +3292,7 @@ namespace {
}
- if (const auto commonType = CommonType<false, true>(input.Pos(), p.first, item->GetItemType(), ctx.Expr)) {
+ if (const auto commonType = CommonType<false, true>(input.Pos(), p.first, item->GetItemType(), ctx.Expr, ctx.Types)) {
p.first = commonType;
++p.second;
continue;
@@ -3413,7 +3413,7 @@ namespace {
return IGraphTransformer::TStatus::Error;
}
for (size_t i = 0; i < childTypes.size(); ++i) {
- if (const auto commonType = CommonType<false>(child->Pos(), resultTypes[i], childTypes[i], ctx.Expr))
+ if (const auto commonType = CommonType<false>(child->Pos(), resultTypes[i], childTypes[i], ctx.Expr, ctx.Types))
resultTypes[i] = commonType;
else
return IGraphTransformer::TStatus::Error;
@@ -3549,7 +3549,7 @@ namespace {
}
if constexpr (!IsStrict) {
- if (const auto commonType = CommonTypeForChildren(*input, ctx.Expr)) {
+ if (const auto commonType = CommonTypeForChildren(*input, ctx.Expr, ctx.Types)) {
if (const auto status = ConvertChildrenToType(input, commonType, ctx.Expr, ctx.Types); status != IGraphTransformer::TStatus::Ok)
return status;
} else
@@ -9770,7 +9770,7 @@ namespace {
return IGraphTransformer::TStatus::Ok;
}
- IGraphTransformer::TStatus SqlCombineWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) {
+ IGraphTransformer::TStatus SqlCombineWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx) {
Y_UNUSED(output);
if (!EnsureArgsCount(*input, 3, ctx.Expr)) {
return IGraphTransformer::TStatus::Error;
@@ -9816,7 +9816,7 @@ namespace {
const auto leftKeyType = leftInput->Child(3U)->GetTypeAnn();
const auto rightKeyType = rightInput->Child(3U)->GetTypeAnn();
- const auto commonKeyType = CommonType<false>(input->Pos(), leftKeyType, rightKeyType, ctx.Expr, /*warn=*/true);
+ const auto commonKeyType = CommonType<false>(input->Pos(), leftKeyType, rightKeyType, ctx.Expr, ctx.Types, /*warn=*/true);
if (!commonKeyType) {
return IGraphTransformer::TStatus::Error;
}
diff --git a/yql/essentials/core/type_ann/type_ann_list.h b/yql/essentials/core/type_ann/type_ann_list.h
index 5a4d8038e74..093fad20f7d 100644
--- a/yql/essentials/core/type_ann/type_ann_list.h
+++ b/yql/essentials/core/type_ann/type_ann_list.h
@@ -13,7 +13,7 @@ namespace NYql::NTypeAnnImpl {
TPositionHandle pos,
const TExprNode::TListType& children,
const TStructExprType*& resultStructType,
- TContext& ctx,
+ TExtContext& ctx,
bool areHashesChecked,
bool& isUniversal);
@@ -73,7 +73,7 @@ namespace NYql::NTypeAnnImpl {
IGraphTransformer::TStatus ChopperWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus HasItemsWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus ExtendWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
- IGraphTransformer::TStatus SelectOpWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
+ IGraphTransformer::TStatus SelectOpWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx);
IGraphTransformer::TStatus SelectOpPositionalWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx);
template <bool IsStrict>
IGraphTransformer::TStatus ListExtendWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx);
@@ -159,6 +159,6 @@ namespace NYql::NTypeAnnImpl {
IGraphTransformer::TStatus TimeOrderRecoverWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus WatermarkGeneratorWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
IGraphTransformer::TStatus SqlCombineInputWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
- IGraphTransformer::TStatus SqlCombineWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx);
+ IGraphTransformer::TStatus SqlCombineWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TExtContext& ctx);
} // namespace NYql::NTypeAnnImpl
diff --git a/yql/essentials/core/type_ann/type_ann_types.cpp b/yql/essentials/core/type_ann/type_ann_types.cpp
index 1329770af7a..08f868b3740 100644
--- a/yql/essentials/core/type_ann/type_ann_types.cpp
+++ b/yql/essentials/core/type_ann/type_ann_types.cpp
@@ -2,12 +2,14 @@
#include "type_ann_expr.h"
#include "type_ann_impl.h"
#include "type_ann_types.h"
+
#include "yql/essentials/core/yql_opt_utils.h"
+#include "yql/essentials/core/langver/feature.gen.h"
namespace NYql::NTypeAnnImpl {
bool CheckLinearLangver(TPositionHandle pos, TLangVersion langver, TExprContext& ctx) {
- if (!IsAvailableLangVersion(MakeLangVersion(2025, 4), langver)) {
+ if (!IsAvailableLangVersion(NFeature::LinearTypes.MinLangVer, langver)) {
ctx.AddError(TIssue(ctx.GetPosition(pos), "Linear types are not available before version 2025.04"));
return false;
}
@@ -2572,4 +2574,3 @@ namespace NYql::NTypeAnnImpl {
return IGraphTransformer::TStatus::Ok;
}
} // namespace NYql::NTypeAnnImpl
-
diff --git a/yql/essentials/core/ut/ya.make b/yql/essentials/core/ut/ya.make
index f07256a31bb..bc15f87f7ca 100644
--- a/yql/essentials/core/ut/ya.make
+++ b/yql/essentials/core/ut/ya.make
@@ -6,6 +6,7 @@ SRCS(
yql_expr_constraint_ut.cpp
yql_range_frame_collector_bounds_ut.cpp
yql_expr_optimize_ut.cpp
+ yql_expr_type_annotation_ut.cpp
yql_library_compiler_ut.cpp
yql_opt_utils_ut.cpp
yql_udf_index_ut.cpp
diff --git a/yql/essentials/core/ut/yql_expr_type_annotation_ut.cpp b/yql/essentials/core/ut/yql_expr_type_annotation_ut.cpp
new file mode 100644
index 00000000000..e418f284a6d
--- /dev/null
+++ b/yql/essentials/core/ut/yql_expr_type_annotation_ut.cpp
@@ -0,0 +1,170 @@
+#include <yql/essentials/core/yql_expr_type_annotation.h>
+
+#include <yql/essentials/core/yql_type_annotation.h>
+#include <yql/essentials/core/type_ann/type_ann_expr.h>
+#include <yql/essentials/ast/yql_expr.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/string/cast.h>
+
+#include <functional>
+
+namespace NYql {
+
+namespace {
+
+TExprNode::TPtr MakeDecimalNode(int precision, int scale, TExprContext& ctx, TTypeAnnotationContext& typesCtx) {
+ // clang-format off
+ auto expr = ctx.Builder(TPositionHandle())
+ .Callable("Decimal")
+ .Atom(0, "0")
+ .Atom(1, ToString(precision))
+ .Atom(2, ToString(scale))
+ .Seal()
+ .Build();
+ // clang-format on
+ ctx.RepeatTransformCounter = 0;
+ if (!InstantAnnotateTypes(expr, ctx, /*wholeProgram=*/false, typesCtx)) {
+ return nullptr;
+ }
+ return expr;
+}
+
+using TCommonTypeFn = std::function<const TTypeAnnotationNode*(TPositionHandle,
+ const TTypeAnnotationNode*,
+ const TTypeAnnotationNode*,
+ TExprContext&,
+ const TTypeAnnotationContext&,
+ bool)>;
+
+TString DescribeDecimal(const TTypeAnnotationNode* type) {
+ const auto* params = static_cast<const TDataExprParamsType*>(type->Cast<TDataExprType>());
+ return TStringBuilder() << "Decimal(" << params->GetParamOne() << "," << params->GetParamTwo() << ")";
+}
+
+void CheckExpectedCommonType(TExprNode::TPtr node1, TExprNode::TPtr node2, TExprContext& ctx, TTypeAnnotationContext& typesCtx, TExprNode::TPtr expected = {}) {
+ YQL_ENSURE(node1);
+ YQL_ENSURE(node2);
+
+ if (!expected) {
+ const auto* common = CommonType<true>(TPositionHandle(), node1->GetTypeAnn(), node2->GetTypeAnn(), ctx, typesCtx, /*warn=*/false);
+ UNIT_ASSERT_C(!common, "Expected CommonType to return null for "
+ << DescribeDecimal(node1->GetTypeAnn()) << " and "
+ << DescribeDecimal(node2->GetTypeAnn()) << " but got non-null");
+ return;
+ }
+
+ const auto* common = CommonType<true>(TPositionHandle(), node1->GetTypeAnn(), node2->GetTypeAnn(), ctx, typesCtx, /*warn=*/false);
+ UNIT_ASSERT_C(common, "CommonType returned null for "
+ << DescribeDecimal(node1->GetTypeAnn()) << " and "
+ << DescribeDecimal(node2->GetTypeAnn()));
+
+ UNIT_ASSERT_C(IsSameAnnotation(*common, *expected->GetTypeAnn()),
+ "CommonType for " << DescribeDecimal(node1->GetTypeAnn())
+ << " and " << DescribeDecimal(node2->GetTypeAnn())
+ << " expected " << DescribeDecimal(expected->GetTypeAnn())
+ << " but got " << DescribeDecimal(common));
+}
+
+void CheckCommonTypeBothCastable(TCommonTypeFn commonTypeFn, const TString& pipelineName) {
+ constexpr int minDecimalTypePart = 0;
+ constexpr int maxDecimalTypePart = 35;
+
+ TExprContext ctx;
+ TTypeAnnotationContext typesCtx;
+ typesCtx.DecimalConversionMode = EDecimalConversionMode::WithCommonTypeFixup;
+
+ for (int i = minDecimalTypePart; i <= maxDecimalTypePart; ++i) {
+ for (int j = minDecimalTypePart; j <= maxDecimalTypePart; ++j) {
+ for (int v = minDecimalTypePart; v <= maxDecimalTypePart; ++v) {
+ for (int b = minDecimalTypePart; b <= maxDecimalTypePart; ++b) {
+ auto node1 = MakeDecimalNode(i, j, ctx, typesCtx);
+ if (!node1) {
+ continue;
+ }
+ auto node2 = MakeDecimalNode(v, b, ctx, typesCtx);
+ if (!node2) {
+ continue;
+ }
+
+ const auto* common = commonTypeFn(TPositionHandle(),
+ node1->GetTypeAnn(), node2->GetTypeAnn(), ctx, typesCtx, false);
+ if (!common) {
+ continue;
+ }
+
+ auto status1 = TrySilentConvertTo(node1, *node1->GetTypeAnn(), *common, ctx, typesCtx);
+ auto status2 = TrySilentConvertTo(node2, *node2->GetTypeAnn(), *common, ctx, typesCtx);
+ const auto* params = static_cast<const TDataExprParamsType*>(common->Cast<TDataExprType>());
+ UNIT_ASSERT_C(status1.Level != IGraphTransformer::TStatus::Error && status2.Level != IGraphTransformer::TStatus::Error,
+ pipelineName << " returned non-null but cast failed."
+ << " Left: Decimal(" << i << "," << j << ")"
+ << ", Right: Decimal(" << v << "," << b << ")"
+ << ", common: Decimal("
+ << params->GetParamOne() << "," << params->GetParamTwo() << ")"
+ << ", status1: " << status1.Level
+ << ", status2: " << status2.Level);
+ }
+ }
+ }
+ }
+}
+
+} // namespace
+
+Y_UNIT_TEST_SUITE(TYqlTryConvertToDecimal) {
+
+Y_UNIT_TEST(CommonTypeBothCastable) {
+ CheckCommonTypeBothCastable(CommonType<true>, "CommonType");
+}
+
+Y_UNIT_TEST(CommonTypeExpectedResults) {
+ TExprContext ctx;
+ TTypeAnnotationContext typesCtx;
+ typesCtx.DecimalConversionMode = EDecimalConversionMode::WithCommonTypeFixup;
+
+ auto dec = [&](int precision, int scale) {
+ return MakeDecimalNode(precision, scale, ctx, typesCtx);
+ };
+ auto check = [&](TExprNode::TPtr node1, TExprNode::TPtr node2, TExprNode::TPtr expected) {
+ CheckExpectedCommonType(std::move(node1), std::move(node2), ctx, typesCtx, std::move(expected));
+ };
+
+ // Same types — result is the same type
+ check(dec(1, 0), dec(1, 0), dec(1, 0));
+ check(dec(1, 1), dec(1, 1), dec(1, 1));
+ check(dec(10, 5), dec(10, 5), dec(10, 5));
+ check(dec(35, 0), dec(35, 0), dec(35, 0));
+ check(dec(35, 35), dec(35, 35), dec(35, 35));
+
+ // Different scales, precision fits
+ check(dec(1, 0), dec(1, 1), dec(2, 1));
+ check(dec(5, 2), dec(7, 4), dec(7, 4));
+ check(dec(2, 1), dec(3, 2), dec(3, 2));
+ check(dec(10, 0), dec(1, 1), dec(11, 1));
+
+ // Precision at boundary (whole + scale = MaxPrecision)
+ check(dec(18, 0), dec(17, 17), dec(35, 17));
+ check(dec(17, 0), dec(18, 18), dec(35, 18));
+ check(dec(20, 2), dec(18, 17), dec(35, 17));
+
+ // Commutativity: order should not matter
+ check(dec(1, 1), dec(1, 0), dec(2, 1));
+ check(dec(7, 4), dec(5, 2), dec(7, 4));
+ check(dec(17, 17), dec(18, 0), dec(35, 17));
+
+ // CommonType returns null — precision overflow
+ check(dec(20, 8), dec(35, 6), {});
+ check(dec(34, 16), dec(33, 18), {});
+ check(dec(35, 7), dec(35, 6), {});
+ check(dec(35, 17), dec(35, 18), {});
+ check(dec(35, 0), dec(35, 35), {});
+ check(dec(35, 1), dec(35, 34), {});
+ check(dec(1, 0), dec(35, 35), {});
+ check(dec(18, 0), dec(18, 18), {});
+}
+
+} // Y_UNIT_TEST_SUITE(TYqlTryConvertToDecimal)
+
+} // namespace NYql
diff --git a/yql/essentials/core/yql_execution.cpp b/yql/essentials/core/yql_execution.cpp
index df1680eff51..1125338bc72 100644
--- a/yql/essentials/core/yql_execution.cpp
+++ b/yql/essentials/core/yql_execution.cpp
@@ -4,6 +4,7 @@
#include "yql_linear_checker.h"
#include <yql/essentials/core/yql_opt_utils.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/utils/log/log.h>
#include <yql/essentials/utils/yql_panic.h>
@@ -993,7 +994,7 @@ TAutoPtr<IGraphTransformer> CreateCheckExecutionTransformer(const TTypeAnnotatio
});
}
- if (!hasErrors && types.LangVer >= MakeLangVersion(2025, 4)) {
+ if (!hasErrors && types.LangVer >= NFeature::LinearTypes.MinLangVer) {
hasErrors = !ValidateLinearTypes(*input, ctx);
}
diff --git a/yql/essentials/core/yql_expr_type_annotation.cpp b/yql/essentials/core/yql_expr_type_annotation.cpp
index 834bb44e392..75fe4547eaa 100644
--- a/yql/essentials/core/yql_expr_type_annotation.cpp
+++ b/yql/essentials/core/yql_expr_type_annotation.cpp
@@ -1470,21 +1470,42 @@ const TTaggedExprType* DryType(const TTaggedExprType* type, bool& hasOptional, T
return nullptr;
}
-template<bool Silent>
-const TDataExprType* CommonType(TPositionHandle pos, const TDataExprType* one, const TDataExprType* two, TExprContext& ctx, bool warn) {
+template <bool Silent>
+const TDataExprType* CommonType(TPositionHandle pos, const TDataExprType* one, const TDataExprType* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn) {
const auto slot1 = one->GetSlot();
const auto slot2 = two->GetSlot();
if (IsDataTypeDecimal(slot1) && IsDataTypeDecimal(slot2)) {
const auto parts1 = GetDecimalParts(*one);
const auto parts2 = GetDecimalParts(*two);
- const auto whole = std::min<ui8>(NDecimal::MaxPrecision, std::max<ui8>(parts1.first - parts1.second, parts2.first - parts2.second));
- const auto scale = std::min<ui8>(NDecimal::MaxPrecision - whole, std::max<ui8>(parts1.second, parts2.second));
+ ui8 whole = 0;
+ ui8 scale = 0;
+ switch (typesCtx.DecimalConversionMode) {
+ case EDecimalConversionMode::WithCommonTypeFixup:
+ whole = std::max<ui8>(parts1.first, parts2.first);
+ scale = std::max<ui8>(parts1.second, parts2.second);
+ if (whole + scale > NDecimal::MaxPrecision) {
+ if constexpr (!Silent) {
+ ctx.AddError(TIssue(ctx.GetPosition(pos),
+ TStringBuilder() << "Cannot infer common type for "
+ << *static_cast<const TTypeAnnotationNode*>(one)
+ << " and "
+ << *static_cast<const TTypeAnnotationNode*>(two)
+ << ": common precision is "
+ << whole + scale << ", that is greater than " << static_cast<ui64>(NDecimal::MaxPrecision)));
+ }
+ return nullptr;
+ }
+ break;
+ case EDecimalConversionMode::WithoutCommonTypeFixup:
+ whole = std::min<ui8>(NDecimal::MaxPrecision, std::max<ui8>(parts1.first - parts1.second, parts2.first - parts2.second));
+ scale = std::min<ui8>(NDecimal::MaxPrecision - whole, std::max<ui8>(parts1.second, parts2.second));
+ break;
+ }
return ctx.MakeType<TDataExprParamsType>(EDataSlot::Decimal, ToString(whole + scale), ToString(scale));
} else if (!(IsDataTypeDecimal(slot1) || IsDataTypeDecimal(slot2))) {
if (const auto super = GetSuperType(slot1, slot2, warn, &ctx, &pos))
return ctx.MakeType<TDataExprType>(*super);
}
-
if constexpr (!Silent)
ctx.AddError(TIssue(ctx.GetPosition(pos), TStringBuilder() << "Cannot infer common type for " << GetDataTypeInfo(slot1).Name << " and " << GetDataTypeInfo(slot2).Name));
return nullptr;
@@ -1536,22 +1557,22 @@ const TResourceExprType* CommonType(TPositionHandle pos, const TResourceExprType
}
template<bool Strict, bool Silent, class SequenceType>
-const SequenceType* CommonItemType(TPositionHandle pos, const SequenceType* one, const SequenceType* two, TExprContext& ctx) {
- if (const auto join = CommonType<Strict, Silent>(pos, one->GetItemType(), two->GetItemType(), ctx))
+const SequenceType* CommonItemType(TPositionHandle pos, const SequenceType* one, const SequenceType* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx) {
+ if (const auto join = CommonType<Strict, Silent>(pos, one->GetItemType(), two->GetItemType(), ctx, typesCtx))
return ctx.MakeType<SequenceType>(join);
return nullptr;
}
template<bool Strict, bool Silent>
-const TDictExprType* CommonType(TPositionHandle pos, const TDictExprType* one, const TDictExprType* two, TExprContext& ctx) {
- if (const auto joinKey = CommonType<Strict, Silent>(pos, one->GetKeyType(), two->GetKeyType(), ctx))
- if (const auto join = CommonType<Strict, Silent>(pos, one->GetPayloadType(), two->GetPayloadType(), ctx))
+const TDictExprType* CommonType(TPositionHandle pos, const TDictExprType* one, const TDictExprType* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx) {
+ if (const auto joinKey = CommonType<Strict, Silent>(pos, one->GetKeyType(), two->GetKeyType(), ctx, typesCtx))
+ if (const auto join = CommonType<Strict, Silent>(pos, one->GetPayloadType(), two->GetPayloadType(), ctx, typesCtx))
return ctx.MakeType<TDictExprType>(joinKey, join);
return nullptr;
}
template<bool Strict, bool Silent, bool Relaxed = false>
-const TStructExprType* CommonType(TPositionHandle pos, const TStructExprType* one, const TStructExprType* two, TExprContext& ctx) {
+const TStructExprType* CommonType(TPositionHandle pos, const TStructExprType* one, const TStructExprType* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx) {
auto itemsOne = one->GetItems();
auto itemsTwo = two->GetItems();
@@ -1595,7 +1616,7 @@ const TStructExprType* CommonType(TPositionHandle pos, const TStructExprType* on
if (name != itemsTwo[i]->GetName())
return nullptr;
- if (const auto join = CommonType<Strict, Silent>(pos, itemsOne[i]->GetItemType(), itemsTwo[i]->GetItemType(), ctx))
+ if (const auto join = CommonType<Strict, Silent>(pos, itemsOne[i]->GetItemType(), itemsTwo[i]->GetItemType(), ctx, typesCtx))
itemsOne[i] = ctx.MakeType<TItemExprType>(name, join);
else
return nullptr;
@@ -1604,7 +1625,7 @@ const TStructExprType* CommonType(TPositionHandle pos, const TStructExprType* on
}
template<bool Strict, bool Silent, bool Relaxed = false>
-const TTupleExprType* CommonType(TPositionHandle pos, const TTupleExprType* one, const TTupleExprType* two, TExprContext& ctx) {
+const TTupleExprType* CommonType(TPositionHandle pos, const TTupleExprType* one, const TTupleExprType* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx) {
auto itemsOne = one->GetItems();
auto itemsTwo = two->GetItems();
@@ -1621,7 +1642,7 @@ const TTupleExprType* CommonType(TPositionHandle pos, const TTupleExprType* one,
if (const auto join = CommonType<Strict, Silent>(pos,
itemsOne[i] ? itemsOne[i] : Relaxed ? itemsTwo[i] : ctx.MakeType<TNullExprType>(),
itemsTwo[i] ? itemsTwo[i] : Relaxed ? itemsOne[i] : ctx.MakeType<TNullExprType>(),
- ctx))
+ ctx, typesCtx))
itemsOne[i] = join;
else
return nullptr;
@@ -1630,7 +1651,7 @@ const TTupleExprType* CommonType(TPositionHandle pos, const TTupleExprType* one,
}
template<bool Strict, bool Silent>
-const TVariantExprType* CommonType(TPositionHandle pos, const TVariantExprType* one, const TVariantExprType* two, TExprContext& ctx) {
+const TVariantExprType* CommonType(TPositionHandle pos, const TVariantExprType* one, const TVariantExprType* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx) {
const auto underOne = one->GetUnderlyingType();
const auto underTwo = two->GetUnderlyingType();
const auto kind = underOne->GetKind();
@@ -1639,11 +1660,11 @@ const TVariantExprType* CommonType(TPositionHandle pos, const TVariantExprType*
switch (kind) {
case ETypeAnnotationKind::Tuple:
- if (const auto dry = CommonType<Strict, Silent, true>(pos, underOne->Cast<TTupleExprType>(), underTwo->Cast<TTupleExprType>(), ctx))
+ if (const auto dry = CommonType<Strict, Silent, true>(pos, underOne->Cast<TTupleExprType>(), underTwo->Cast<TTupleExprType>(), ctx, typesCtx))
return ctx.MakeType<TVariantExprType>(dry);
break;
case ETypeAnnotationKind::Struct:
- if (const auto dry = CommonType<Strict, Silent, true>(pos, underOne->Cast<TStructExprType>(), underTwo->Cast<TStructExprType>(), ctx))
+ if (const auto dry = CommonType<Strict, Silent, true>(pos, underOne->Cast<TStructExprType>(), underTwo->Cast<TStructExprType>(), ctx, typesCtx))
return ctx.MakeType<TVariantExprType>(dry);
break;
default:
@@ -1653,13 +1674,13 @@ const TVariantExprType* CommonType(TPositionHandle pos, const TVariantExprType*
}
template<bool Strict, bool Silent>
-const TTaggedExprType* CommonType(TPositionHandle pos, const TTaggedExprType* one, const TTaggedExprType* two, TExprContext& ctx) {
+const TTaggedExprType* CommonType(TPositionHandle pos, const TTaggedExprType* one, const TTaggedExprType* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx) {
const auto& tag = one->GetTag();
if (two->GetTag() != tag) {
ctx.AddError(TIssue(ctx.GetPosition(pos), TStringBuilder() << "Different tags '" << tag << "' and '" << two->GetTag() << "'."));
return nullptr;
}
- if (const auto join = CommonType<Strict, Silent>(pos, one->GetBaseType(), two->GetBaseType(), ctx))
+ if (const auto join = CommonType<Strict, Silent>(pos, one->GetBaseType(), two->GetBaseType(), ctx, typesCtx))
return ctx.MakeType<TTaggedExprType>(join, tag);
return nullptr;
}
@@ -1871,7 +1892,7 @@ const TTypeAnnotationNode* JoinDryKeyType(const TTypeAnnotationNode* primary, co
return nullptr;
}
-const TTypeAnnotationNode* JoinCommonDryKeyType(TPositionHandle position, bool outer, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx) {
+const TTypeAnnotationNode* JoinCommonDryKeyType(TPositionHandle position, bool outer, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx) {
bool optOne = false;
bool optTwo = false;
auto dryOne = DryType(one, optOne, ctx);
@@ -1884,11 +1905,11 @@ const TTypeAnnotationNode* JoinCommonDryKeyType(TPositionHandle position, bool o
dryTwo = ctx.MakeType<TOptionalExprType>(dryTwo);
}
- return CommonType<true, false>(position, dryOne, dryTwo, ctx);
+ return CommonType<true, false>(position, dryOne, dryTwo, ctx, typesCtx);
}
template<bool Strict, bool Silent>
-const TTypeAnnotationNode* CommonType(TPositionHandle pos, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, bool warn) {
+const TTypeAnnotationNode* CommonType(TPositionHandle pos, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn) {
if (!(one && two))
return nullptr;
@@ -1902,25 +1923,25 @@ const TTypeAnnotationNode* CommonType(TPositionHandle pos, const TTypeAnnotation
if (const auto kindOne = one->GetKind(), kindTwo = two->GetKind(); kindOne == kindTwo) {
switch (kindOne) {
case ETypeAnnotationKind::Data:
- return CommonType<Silent>(pos, one->Cast<TDataExprType>(), two->Cast<TDataExprType>(), ctx, warn);
+ return CommonType<Silent>(pos, one->Cast<TDataExprType>(), two->Cast<TDataExprType>(), ctx, typesCtx, warn);
case ETypeAnnotationKind::Optional:
- return CommonItemType<Strict, Silent>(pos, one->Cast<TOptionalExprType>(), two->Cast<TOptionalExprType>(), ctx);
+ return CommonItemType<Strict, Silent>(pos, one->Cast<TOptionalExprType>(), two->Cast<TOptionalExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::List:
- return CommonItemType<Strict, Silent>(pos, one->Cast<TListExprType>(), two->Cast<TListExprType>(), ctx);
+ return CommonItemType<Strict, Silent>(pos, one->Cast<TListExprType>(), two->Cast<TListExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::Flow:
- return CommonItemType<Strict, Silent>(pos, one->Cast<TFlowExprType>(), two->Cast<TFlowExprType>(), ctx);
+ return CommonItemType<Strict, Silent>(pos, one->Cast<TFlowExprType>(), two->Cast<TFlowExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::Stream:
- return CommonItemType<Strict, Silent>(pos, one->Cast<TStreamExprType>(), two->Cast<TStreamExprType>(), ctx);
+ return CommonItemType<Strict, Silent>(pos, one->Cast<TStreamExprType>(), two->Cast<TStreamExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::Dict:
- return CommonType<Strict, Silent>(pos, one->Cast<TDictExprType>(), two->Cast<TDictExprType>(), ctx);
+ return CommonType<Strict, Silent>(pos, one->Cast<TDictExprType>(), two->Cast<TDictExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::Tuple:
- return CommonType<Strict, Silent>(pos, one->Cast<TTupleExprType>(), two->Cast<TTupleExprType>(), ctx);
+ return CommonType<Strict, Silent>(pos, one->Cast<TTupleExprType>(), two->Cast<TTupleExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::Struct:
- return CommonType<Strict, Silent>(pos, one->Cast<TStructExprType>(), two->Cast<TStructExprType>(), ctx);
+ return CommonType<Strict, Silent>(pos, one->Cast<TStructExprType>(), two->Cast<TStructExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::Variant:
- return CommonType<Strict, Silent>(pos, one->Cast<TVariantExprType>(), two->Cast<TVariantExprType>(), ctx);
+ return CommonType<Strict, Silent>(pos, one->Cast<TVariantExprType>(), two->Cast<TVariantExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::Tagged:
- return CommonType<Strict, Silent>(pos, one->Cast<TTaggedExprType>(), two->Cast<TTaggedExprType>(), ctx);
+ return CommonType<Strict, Silent>(pos, one->Cast<TTaggedExprType>(), two->Cast<TTaggedExprType>(), ctx, typesCtx);
case ETypeAnnotationKind::Pg:
return CommonType<Silent>(pos, one->Cast<TPgExprType>(), two->Cast<TPgExprType>(), ctx);
default:
@@ -1956,12 +1977,12 @@ const TTypeAnnotationNode* CommonType(TPositionHandle pos, const TTypeAnnotation
} else if (ETypeAnnotationKind::Optional == kindOne) {
if (ETypeAnnotationKind::Null == kindTwo)
return one;
- else if (const auto itemType = CommonType<Strict, Silent>(pos, one->Cast<TOptionalExprType>()->GetItemType(), two, ctx))
+ else if (const auto itemType = CommonType<Strict, Silent>(pos, one->Cast<TOptionalExprType>()->GetItemType(), two, ctx, typesCtx))
return ctx.MakeType<TOptionalExprType>(itemType);
} else if (ETypeAnnotationKind::Optional == kindTwo) {
if (ETypeAnnotationKind::Null == kindOne)
return two;
- else if (const auto itemType = CommonType<Strict, Silent>(pos, one, two->Cast<TOptionalExprType>()->GetItemType(), ctx))
+ else if (const auto itemType = CommonType<Strict, Silent>(pos, one, two->Cast<TOptionalExprType>()->GetItemType(), ctx, typesCtx))
return ctx.MakeType<TOptionalExprType>(itemType);
} else if (ETypeAnnotationKind::Null == kindOne) {
return ctx.MakeType<TOptionalExprType>(two);
@@ -1989,24 +2010,24 @@ const TTypeAnnotationNode* CommonType(TPositionHandle pos, const TTypeAnnotation
return nullptr;
}
-template const TTypeAnnotationNode* CommonType<true, false>(TPositionHandle pos, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, bool warn);
-template const TTypeAnnotationNode* CommonType<false, false>(TPositionHandle pos, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, bool warn);
-template const TTypeAnnotationNode* CommonType<false, true>(TPositionHandle pos, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, bool warn);
+template const TTypeAnnotationNode* CommonType<true, false>(TPositionHandle pos, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn);
+template const TTypeAnnotationNode* CommonType<false, false>(TPositionHandle pos, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn);
+template const TTypeAnnotationNode* CommonType<false, true>(TPositionHandle pos, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn);
-const TTypeAnnotationNode* CommonType(TPositionHandle position, const TTypeAnnotationNode::TSpanType& types, TExprContext& ctx, bool warn) {
+const TTypeAnnotationNode* CommonType(TPositionHandle position, const TTypeAnnotationNode::TSpanType& types, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn) {
switch (types.size()) {
case 0U: return nullptr;
case 1U: return types.front();
- case 2U: return CommonType<false, false>(position, types.front(), types.back(), ctx, warn);
+ case 2U: return CommonType<false, false>(position, types.front(), types.back(), ctx, typesCtx, warn);
default: break;
}
const auto left = types.size() >> 1U;
const auto right = types.size() - left;
- return CommonType<false, false>(position, CommonType(position, types.first(left), ctx, warn), CommonType(position, types.last(right), ctx, warn), ctx, warn);
+ return CommonType<false, false>(position, CommonType(position, types.first(left), ctx, typesCtx, warn), CommonType(position, types.last(right), ctx, typesCtx, warn), ctx, typesCtx, warn);
}
-const TTypeAnnotationNode* CommonTypeForChildren(const TExprNode& node, TExprContext& ctx, bool warn) {
+const TTypeAnnotationNode* CommonTypeForChildren(const TExprNode& node, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn) {
TTypeAnnotationNode::TListType types(node.ChildrenSize());
for (auto i = 0U; i < types.size(); ++i) {
if (const auto item = node.Child(i); EnsureComputable(*item, ctx))
@@ -2014,7 +2035,7 @@ const TTypeAnnotationNode* CommonTypeForChildren(const TExprNode& node, TExprCon
else
return nullptr;
}
- return CommonType(node.Pos(), types, ctx, warn);
+ return CommonType(node.Pos(), types, ctx, typesCtx, warn);
}
size_t GetOptionalLevel(const TTypeAnnotationNode* type) {
diff --git a/yql/essentials/core/yql_expr_type_annotation.h b/yql/essentials/core/yql_expr_type_annotation.h
index 452a430b951..3310fb860db 100644
--- a/yql/essentials/core/yql_expr_type_annotation.h
+++ b/yql/essentials/core/yql_expr_type_annotation.h
@@ -238,13 +238,13 @@ const TTypeAnnotationNode* DryType(const TTypeAnnotationNode* type, bool& hasOpt
const TTypeAnnotationNode* JoinDryKeyType(bool outer, const TTypeAnnotationNode* primary, const TTypeAnnotationNode* secondary, TExprContext& ctx);
const TTypeAnnotationNode* JoinDryKeyType(const TTypeAnnotationNode* primary, const TTypeAnnotationNode* secondary, bool& hasOptional, TExprContext& ctx);
// Key type for inner or full join.
-const TTypeAnnotationNode* JoinCommonDryKeyType(TPositionHandle position, bool outer, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx);
+const TTypeAnnotationNode* JoinCommonDryKeyType(TPositionHandle position, bool outer, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx);
template <bool Strict, bool Silent = false> // Strict + DryType before - common type for join key.
-const TTypeAnnotationNode* CommonType(TPositionHandle position, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, bool warn = false);
+const TTypeAnnotationNode* CommonType(TPositionHandle position, const TTypeAnnotationNode* one, const TTypeAnnotationNode* two, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn = false);
-const TTypeAnnotationNode* CommonType(TPositionHandle position, const TTypeAnnotationNode::TSpanType& types, TExprContext& ctx, bool warn = false);
-const TTypeAnnotationNode* CommonTypeForChildren(const TExprNode& node, TExprContext& ctx, bool warn = false);
+const TTypeAnnotationNode* CommonType(TPositionHandle position, const TTypeAnnotationNode::TSpanType& types, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn = false);
+const TTypeAnnotationNode* CommonTypeForChildren(const TExprNode& node, TExprContext& ctx, const TTypeAnnotationContext& typesCtx, bool warn = false);
size_t GetOptionalLevel(const TTypeAnnotationNode* type);
diff --git a/yql/essentials/core/yql_join.cpp b/yql/essentials/core/yql_join.cpp
index caa7ac17e2c..d1d43b83101 100644
--- a/yql/essentials/core/yql_join.cpp
+++ b/yql/essentials/core/yql_join.cpp
@@ -951,7 +951,8 @@ IGraphTransformer::TStatus EquiJoinAnnotation(
const TJoinLabels& labels,
TExprNode& joins,
const TJoinOptions& options,
- TExprContext& ctx
+ TExprContext& ctx,
+ const TTypeAnnotationContext& typesCtx
) {
auto position = ctx.GetPosition(positionHandle);
@@ -1041,7 +1042,7 @@ IGraphTransformer::TStatus EquiJoinAnnotation(
if (options.Flatten) {
for (auto& x : flattenFields) {
- if (const auto commonType = CommonType(positionHandle, x.second.AllTypes, ctx)) {
+ if (const auto commonType = CommonType(positionHandle, x.second.AllTypes, ctx, typesCtx)) {
const bool unwrap = ETypeAnnotationKind::Optional == commonType->GetKind() &&
std::any_of(x.second.AllTypes.cbegin(), x.second.AllTypes.cend(), [](const TTypeAnnotationNode* type) { return ETypeAnnotationKind::Optional != type->GetKind(); });
resultFields.emplace_back(ctx.MakeType<TItemExprType>(x.first, unwrap ? commonType->Cast<TOptionalExprType>()->GetItemType() : commonType));
diff --git a/yql/essentials/core/yql_join.h b/yql/essentials/core/yql_join.h
index c9d4a22b9a7..70aad7d2c34 100644
--- a/yql/essentials/core/yql_join.h
+++ b/yql/essentials/core/yql_join.h
@@ -77,7 +77,8 @@ IGraphTransformer::TStatus EquiJoinAnnotation(
const TJoinLabels& labels,
TExprNode& joins,
const TJoinOptions& options,
- TExprContext& ctx
+ TExprContext& ctx,
+ const TTypeAnnotationContext& typesCtx
);
IGraphTransformer::TStatus EquiJoinConstraints(
diff --git a/yql/essentials/core/yql_sql_combine_expander.cpp b/yql/essentials/core/yql_sql_combine_expander.cpp
index 62691e26701..9d52cfad700 100644
--- a/yql/essentials/core/yql_sql_combine_expander.cpp
+++ b/yql/essentials/core/yql_sql_combine_expander.cpp
@@ -330,22 +330,18 @@ TExprNode::TPtr ExpandSqlCombine(const TExprNode::TPtr& node, TExprContext& ctx,
.Seal().Build();
const auto result = ctx.Builder(pos)
- .Callable("PartitionsByKeys")
+ .Callable("LPartitionsByKeys")
.Add(0, std::move(merged))
.Add(1, partitionKeySelector)
.Add(2, std::move(presortDirection))
.Add(3, std::move(presortKeySelector))
.Lambda(4)
- .Param("partitionList")
- .Callable("ForwardList")
- .Callable(0, "Chopper")
- .Callable(0, "ToStream")
- .Arg(0, "partitionList")
- .Seal()
- .Add(1, partitionKeySelector)
- .Add(2, std::move(groupSwitch))
- .Add(3, std::move(chopperHandler))
- .Seal()
+ .Param("partitionStream")
+ .Callable("Chopper")
+ .Arg(0, "partitionStream")
+ .Add(1, partitionKeySelector)
+ .Add(2, std::move(groupSwitch))
+ .Add(3, std::move(chopperHandler))
.Seal()
.Seal()
.Seal().Build();
diff --git a/yql/essentials/core/yql_type_annotation.h b/yql/essentials/core/yql_type_annotation.h
index 0011c94e80d..286f6cf8db5 100644
--- a/yql/essentials/core/yql_type_annotation.h
+++ b/yql/essentials/core/yql_type_annotation.h
@@ -356,6 +356,11 @@ enum class EBlockEngineMode {
Force /* "force" */,
};
+enum class EDecimalConversionMode {
+ WithoutCommonTypeFixup /* "without_common_type_fixup" */,
+ WithCommonTypeFixup /* "with_common_type_fixup" */,
+};
+
enum class EEngineType {
Default /* "default" */,
Dq /* "dq" */,
@@ -493,6 +498,7 @@ struct TTypeAnnotationContext: public TThrRefBase {
ui32 FolderSubDirsLimit = 1000;
bool UseBlocks = false;
EBlockEngineMode BlockEngineMode = EBlockEngineMode::Disable;
+ EDecimalConversionMode DecimalConversionMode = EDecimalConversionMode::WithoutCommonTypeFixup;
THashMap<TString, size_t> NoBlockRewriteCallableStats;
THashMap<TString, size_t> NoBlockRewriteTypeStats;
TMaybe<bool> PgEmitAggApply;
diff --git a/yql/essentials/core/yql_window_features.cpp b/yql/essentials/core/yql_window_features.cpp
index d3fd92554e5..cf6e13260e6 100644
--- a/yql/essentials/core/yql_window_features.cpp
+++ b/yql/essentials/core/yql_window_features.cpp
@@ -2,12 +2,14 @@
#include <yql/essentials/core/yql_opt_utils.h>
#include <yql/essentials/core/yql_type_annotation.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/minikql/mkql_runtime_version.h>
namespace NYql {
bool IsRangeWindowFrameEnabled(TTypeAnnotationContext& types) {
- return IsWindowNewPipelineEnabled(types) && types.LangVer >= MakeLangVersion(2026, 1);
+ return IsWindowNewPipelineEnabled(types) &&
+ types.LangVer >= NFeature::YqlRangeWindows.MinLangVer;
}
bool IsWindowNewPipelineEnabled(TTypeAnnotationContext& types) {
diff --git a/yql/essentials/data/language/features.json b/yql/essentials/data/language/features.json
index ebd095383c1..1cb27b27cb6 100644
--- a/yql/essentials/data/language/features.json
+++ b/yql/essentials/data/language/features.json
@@ -1,8 +1,4 @@
{
- "Materialize": {
- "description": "MATERIALIZE",
- "min_langver": "2026.02"
- },
"Experiments": {
"min_langver": "max"
},
@@ -29,9 +25,28 @@
"YqlSelect": {
"min_langver": "max"
},
+ "Materialize": {
+ "description": "MATERIALIZE",
+ "min_langver": "2026.02"
+ },
+ "HybridRankFunction": {
+ "min_langver": "2026.02"
+ },
"YqlRangeWindows": {
"min_langver": "2026.01"
},
+ "PgPragmaWarning": {
+ "min_langver": "2026.01"
+ },
+ "PositionOfWithIssueFunction": {
+ "min_langver": "2026.01"
+ },
+ "AsOptionalFunction": {
+ "min_langver": "2026.01"
+ },
+ "YsonUtf8": {
+ "min_langver":"2026.01"
+ },
"YtCreateView": {
"description": "CREATE VIEW",
"min_langver": "2025.05"
@@ -40,6 +55,27 @@
"description": "DROP VIEW",
"min_langver": "2025.05"
},
+ "LinearDestroy": {
+ "min_langver": "2025.05"
+ },
+ "FullTextFunction": {
+ "min_langver": "2025.05"
+ },
+ "WriteOffsetWithColon": {
+ "min_langver": "2025.05"
+ },
+ "DateTimeFormatZ": {
+ "min_langver": "2025.05"
+ },
+ "YsonConvert": {
+ "min_langver": "2025.05"
+ },
+ "YsonMut": {
+ "min_langver": "2025.05"
+ },
+ "YsonIterate": {
+ "min_langver": "2025.05"
+ },
"InlineSubquery": {
"description": "Inline subquery",
"min_langver": "2025.04"
@@ -56,10 +92,28 @@
"description": "PARTITIONS table function",
"min_langver": "2025.04"
},
+ "ConcatFunction": {
+ "min_langver": "2025.04"
+ },
+ "NullIfFunction": {
+ "min_langver": "2025.04"
+ },
+ "SimplePgFunction": {
+ "min_langver": "2025.04"
+ },
+ "RandomValueSampleFunction": {
+ "min_langver": "2025.04"
+ },
"ReplaceInto": {
"description": "REPLACE",
"min_langver": "2025.04"
},
+ "LinearTypes": {
+ "min_langver": "2025.04"
+ },
+ "SideEffects": {
+ "min_langver": "2025.04"
+ },
"LegacyNotNull": {
"max_langver": "2025.03"
},
@@ -78,7 +132,37 @@
"PersistableFlattenAndAggrExprs": {
"min_langver": "2025.03"
},
+ "FlattenOptionalByNonOptional": {
+ "min_langver": "2025.03"
+ },
+ "Sha512": {
+ "min_langver": "2025.03"
+ },
+ "IPv4Uint32": {
+ "min_langver": "2025.03"
+ },
+ "SwapBytes": {
+ "min_langver": "2025.03"
+ },
+ "ValidateRegexp": {
+ "min_langver": "2025.03"
+ },
"GroupByExprAfterWhere": {
"min_langver": "2025.02"
+ },
+ "StringReverseBytes": {
+ "min_langver": "2025.02"
+ },
+ "StringAsciiEqualsContainsIgnoreCase": {
+ "min_langver": "2025.02"
+ },
+ "StringAsciiPrefixSuffixIgnoreCase": {
+ "min_langver": "2025.01"
+ },
+ "StringReverse": {
+ "max_langver": "2025.01"
+ },
+ "StringTwoArgs": {
+ "max_langver": "2025.01"
}
}
diff --git a/yql/essentials/parser/pg_wrapper/ubsan.supp b/yql/essentials/parser/pg_wrapper/ubsan.supp
index 7e19d3f56f4..1d5e5352044 100644
--- a/yql/essentials/parser/pg_wrapper/ubsan.supp
+++ b/yql/essentials/parser/pg_wrapper/ubsan.supp
@@ -3,6 +3,7 @@
# Just ignore it instead of fixing since it is third party library.
# It might be fixed in future versions of Postgres.
function:pg_wrapper/postgresql/src/backend/utils/hash/dynahash.c
+function:pg_wrapper/postgresql/src/backend/utils/adt/formatting.c
# Here simple integer overflow in third_party library.
# Just ignore it instead of fixing since it is third party library.
# It might be fixed in future versions of Postgres.
diff --git a/yql/essentials/providers/config/yql_config_provider.cpp b/yql/essentials/providers/config/yql_config_provider.cpp
index af9bfb1d10f..ae3436f7879 100644
--- a/yql/essentials/providers/config/yql_config_provider.cpp
+++ b/yql/essentials/providers/config/yql_config_provider.cpp
@@ -1022,6 +1022,17 @@ private:
ctx.AddError(TIssue(pos, TStringBuilder() << "Expected `disable|auto|force', but got: " << args[0]));
return false;
}
+ } else if (name == "DecimalCommonTypeConversionMode") {
+ if (args.size() != 1) {
+ ctx.AddError(TIssue(pos, TStringBuilder() << "Expected at most 1 argument, but got " << args.size()));
+ return false;
+ }
+
+ auto arg = TString{args[0]};
+ if (!TryFromString(arg, Types_.DecimalConversionMode)) {
+ ctx.AddError(TIssue(pos, TStringBuilder() << "Expected `without_common_type_fixup|with_common_type_fixup', but got: " << args[0]));
+ return false;
+ }
} else if (name == "OptimizerFlags") {
for (auto& arg : args) {
if (arg.empty()) {
diff --git a/yql/essentials/sql/pg/pg_sql.cpp b/yql/essentials/sql/pg/pg_sql.cpp
index 9ea2a53e936..81b9eb396ee 100644
--- a/yql/essentials/sql/pg/pg_sql.cpp
+++ b/yql/essentials/sql/pg/pg_sql.cpp
@@ -40,6 +40,7 @@ extern "C" {
#include <yql/essentials/providers/common/provider/yql_provider_names.h>
#include <yql/essentials/minikql/mkql_type_builder.h>
#include <yql/essentials/core/issue/yql_issue.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/public/issue/yql_warning.h>
#include <yql/essentials/core/sql_types/yql_callable_names.h>
#include <yql/essentials/utils/log/log_level.h>
@@ -2517,7 +2518,7 @@ public:
return nullptr;
}
} else if (name == "warning") {
- if (auto langver = NYql::MakeLangVersion(2026, 01);
+ if (auto langver = NYql::NFeature::PgPragmaWarning.MinLangVer;
!NYql::IsBackwardCompatibleFeatureAvailable(
Settings_.LangVer, langver, Settings_.BackportMode))
{
diff --git a/yql/essentials/sql/pg/pg_sql_ut.cpp b/yql/essentials/sql/pg/pg_sql_ut.cpp
index 2d07350d6ea..18477d3a807 100644
--- a/yql/essentials/sql/pg/pg_sql_ut.cpp
+++ b/yql/essentials/sql/pg/pg_sql_ut.cpp
@@ -1,5 +1,7 @@
#include "ut/util.h"
+#include <yql/essentials/core/langver/feature.gen.h>
+
#include <library/cpp/testing/unittest/registar.h>
#include <yql/essentials/parser/pg_wrapper/interface/parser.h>
@@ -1138,7 +1140,7 @@ Y_UNIT_TEST(WarningAsError) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1155,7 +1157,7 @@ Y_UNIT_TEST(WarningDisable) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(res.Root);
UNIT_ASSERT_EQUAL(res.Issues.Size(), 0);
@@ -1168,7 +1170,7 @@ Y_UNIT_TEST(WarningDefault) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1184,7 +1186,7 @@ Y_UNIT_TEST(WarningInvalidAction) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1200,7 +1202,7 @@ Y_UNIT_TEST(WarningInvalidPattern) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1216,7 +1218,7 @@ Y_UNIT_TEST(WarningMissingArguments) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1232,7 +1234,7 @@ Y_UNIT_TEST(WarningTooManyArguments) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1249,7 +1251,7 @@ Y_UNIT_TEST(WarningSpecificCode) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1266,7 +1268,7 @@ Y_UNIT_TEST(WarningSpecificCodeDisable) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(res.Root);
UNIT_ASSERT_EQUAL(res.Issues.Size(), 0);
@@ -1278,7 +1280,7 @@ Y_UNIT_TEST(WarningNonStringAction) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1294,7 +1296,7 @@ Y_UNIT_TEST(WarningNonStringPattern) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1312,7 +1314,7 @@ Y_UNIT_TEST(WarningMultipleRules) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1328,7 +1330,7 @@ Y_UNIT_TEST(WarningNoArguments) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
diff --git a/yql/essentials/sql/pg/ya.make b/yql/essentials/sql/pg/ya.make
index ab493603bf5..edbcfa2aa30 100644
--- a/yql/essentials/sql/pg/ya.make
+++ b/yql/essentials/sql/pg/ya.make
@@ -6,6 +6,7 @@ PROVIDES(
PEERDIR(
yql/essentials/ast
+ yql/essentials/core/langver
yql/essentials/core/sql_types
yql/essentials/parser/pg_catalog
yql/essentials/minikql
diff --git a/yql/essentials/sql/v1/builtin.cpp b/yql/essentials/sql/v1/builtin.cpp
index 43eeb6af9b4..8fcb3ae0945 100644
--- a/yql/essentials/sql/v1/builtin.cpp
+++ b/yql/essentials/sql/v1/builtin.cpp
@@ -10,6 +10,7 @@
#include <yql/essentials/ast/yql_type_string.h>
#include <yql/essentials/public/udf/udf_data_type.h>
#include <yql/essentials/core/sql_types/simple_types.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/minikql/mkql_program_builder.h>
#include <yql/essentials/minikql/mkql_type_ops.h>
#include <yql/essentials/public/issue/yql_issue_id.h>
@@ -3143,7 +3144,7 @@ struct TBuiltinFuncData {
{"byteat", {"ByteAt", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ByteAt", 2, 2)}},
{"startswith", {"StartsWith", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("StartsWith", 2, 2)}},
{"endswith", {"EndsWith", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EndsWith", 2, 2)}},
- {"concat", {"Concat", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("SqlConcat", 1, -1), NYql::MakeLangVersion(2025, 4)}},
+ {"concat", {"Concat", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("SqlConcat", 1, -1), NYql::NFeature::ConcatFunction.MinLangVer}},
// Numeric builtins
{"abs", {"Abs", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Abs", 1, 1)}},
@@ -3235,30 +3236,30 @@ struct TBuiltinFuncData {
{"dictlookup", {"DictLookup", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Lookup", 2, 2)}},
{"dictcontains", {"DictContains", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Contains", 2, 2)}},
- {"todynamiclinear", {"ToDynamicLinear", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ToDynamicLinear", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"fromdynamiclinear", {"FromDynamicLinear", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromDynamicLinear", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"lineardestroy", {"LinearDestroy", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearDestroy", 1, -1), NYql::MakeLangVersion(2025, 5)}},
+ {"todynamiclinear", {"ToDynamicLinear", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ToDynamicLinear", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"fromdynamiclinear", {"FromDynamicLinear", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromDynamicLinear", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"lineardestroy", {"LinearDestroy", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearDestroy", 1, -1), NYql::NFeature::LinearDestroy.MinLangVer}},
// MutDict builtins
- {"mutdictcreate", {"MutDictCreate", "Normal", BuildSimpleBuiltinFactoryCallback<TMutDictCreateBuiltin>(), NYql::MakeLangVersion(2025, 4)}},
- {"tomutdict", {"ToMutDict", "Normal", BuildSimpleBuiltinFactoryCallback<TToMutDictBuiltin>(), NYql::MakeLangVersion(2025, 4)}},
- {"frommutdict", {"FromMutDict", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromMutDict", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictinsert", {"MutDictInsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictInsert", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictupsert", {"MutDictUpsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictUpsert", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictupdate", {"MutDictUpdate", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictUpdate", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictremove", {"MutDictRemove", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictRemove", 2, 2), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictpop", {"MutDictPop", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictPop", 2, 2), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictcontains", {"MutDictContains", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictContains", 2, 2), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictlookup", {"MutDictLookup", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictLookup", 2, 2), NYql::MakeLangVersion(2025, 4)}},
- {"mutdicthasitems", {"MutDictHasItems", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictHasItems", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictlength", {"MutDictLength", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictLength", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictitems", {"MutDictItems", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictItems", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictkeys", {"MutDictKeys", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictKeys", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictpayloads", {"MutDictPayloads", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictPayloads", 1, 1), NYql::MakeLangVersion(2025, 4)}},
+ {"mutdictcreate", {"MutDictCreate", "Normal", BuildSimpleBuiltinFactoryCallback<TMutDictCreateBuiltin>(), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"tomutdict", {"ToMutDict", "Normal", BuildSimpleBuiltinFactoryCallback<TToMutDictBuiltin>(), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"frommutdict", {"FromMutDict", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromMutDict", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictinsert", {"MutDictInsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictInsert", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictupsert", {"MutDictUpsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictUpsert", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictupdate", {"MutDictUpdate", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictUpdate", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictremove", {"MutDictRemove", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictRemove", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictpop", {"MutDictPop", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictPop", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictcontains", {"MutDictContains", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictContains", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictlookup", {"MutDictLookup", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictLookup", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdicthasitems", {"MutDictHasItems", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictHasItems", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictlength", {"MutDictLength", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictLength", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictitems", {"MutDictItems", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictItems", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictkeys", {"MutDictKeys", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictKeys", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictpayloads", {"MutDictPayloads", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictPayloads", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
- {"dictinsert", {"DictInsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictInsert", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"dictupsert", {"DictUpsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictUpsert", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"dictupdate", {"DictUpdate", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictUpdate", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"dictremove", {"DictRemove", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictRemove", 2, 2), NYql::MakeLangVersion(2025, 4)}},
+ {"dictinsert", {"DictInsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictInsert", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dictupsert", {"DictUpsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictUpsert", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dictupdate", {"DictUpdate", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictUpdate", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dictremove", {"DictRemove", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictRemove", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
// Atom builtins
{"asatom", {"AsAtom", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlAsAtom>()}},
@@ -3280,8 +3281,8 @@ struct TBuiltinFuncData {
{"ensuretype", {"EnsureType", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlTypeAssert<true>>()}},
{"ensureconvertibleto", {"EnsureConvertibleTo", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlTypeAssert<false>>()}},
{"ensure", {"Ensure", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EnsureWarn", 2, 3)}},
- {"withsideeffects", {"WithSideEffects", "Normal", BuildSimpleBuiltinFactoryCallback<TSideEffects<false>>(), NYql::MakeLangVersion(2025, 4)}},
- {"withsideeffectsmode", {"WithSideEffectsMode", "Normal", BuildSimpleBuiltinFactoryCallback<TSideEffects<true>>(), NYql::MakeLangVersion(2025, 4)}},
+ {"withsideeffects", {"WithSideEffects", "Normal", BuildSimpleBuiltinFactoryCallback<TSideEffects<false>>(), NYql::NFeature::SideEffects.MinLangVer}},
+ {"withsideeffectsmode", {"WithSideEffectsMode", "Normal", BuildSimpleBuiltinFactoryCallback<TSideEffects<true>>(), NYql::NFeature::SideEffects.MinLangVer}},
{"evaluateexpr", {"EvaluateExpr", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EvaluateExpr", 1, 1)}},
{"evaluateatom", {"EvaluateAtom", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EvaluateAtom", 1, 1)}},
{"evaluatetype", {"EvaluateType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EvaluateType", 1, 1)}},
@@ -3306,12 +3307,12 @@ struct TBuiltinFuncData {
{"pgarray", {"PgArray", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("PgArray", 1, -1)}},
{"typeof", {"TypeOf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("TypeOf", 1, 1)}},
{"instanceof", {"InstanceOf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("InstanceOf", 1, 1)}},
- {"positionof", {"PositionOf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("PositionOf", 1, 1), NYql::MakeLangVersion(2026, 1)}},
- {"withissue", {"WithIssue", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlWithIssue>(), NYql::MakeLangVersion(2026, 1)}},
+ {"positionof", {"PositionOf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("PositionOf", 1, 1), NYql::NFeature::PositionOfWithIssueFunction.MinLangVer}},
+ {"withissue", {"WithIssue", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlWithIssue>(), NYql::NFeature::PositionOfWithIssueFunction.MinLangVer}},
{"datatype", {"DataType", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlDataType>()}},
{"optionaltype", {"OptionalType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("OptionalType", 1, 1)}},
- {"asoptionaltype", {"AsOptionalType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AsOptionalType", 1, 1), NYql::MakeLangVersion(2026, 1)}},
- {"asoptional", {"AsOptional", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AsOptional", 1, 1), NYql::MakeLangVersion(2026, 1)}},
+ {"asoptionaltype", {"AsOptionalType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AsOptionalType", 1, 1), NYql::NFeature::AsOptionalFunction.MinLangVer}},
+ {"asoptional", {"AsOptional", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AsOptional", 1, 1), NYql::NFeature::AsOptionalFunction.MinLangVer}},
{"listtype", {"ListType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ListType", 1, 1)}},
{"streamtype", {"StreamType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("StreamType", 1, 1)}},
{"dicttype", {"DictType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictType", 2, 2)}},
@@ -3326,9 +3327,9 @@ struct TBuiltinFuncData {
{"taggedtype", {"TaggedType", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlTaggedType>()}},
{"varianttype", {"VariantType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("VariantType", 1, 1)}},
{"callabletype", {"CallableType", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlCallableType>()}},
- {"lineartype", {"LinearType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearType", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"dynamiclineartype", {"DynamicLinearType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DynamicLinearType", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"linearitemtype", {"LinearItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearItemType", 1, 1), NYql::MakeLangVersion(2025, 4)}},
+ {"lineartype", {"LinearType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearType", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dynamiclineartype", {"DynamicLinearType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DynamicLinearType", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"linearitemtype", {"LinearItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearItemType", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
{"optionalitemtype", {"OptionalItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("OptionalItemType", 1, 1)}},
{"listitemtype", {"ListItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ListItemType", 1, 1)}},
{"streamitemtype", {"ListItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("StreamItemType", 1, 1)}},
@@ -3363,8 +3364,8 @@ struct TBuiltinFuncData {
{"datatypecomponents", {"DataTypeComponents", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DataTypeComponents", 1, 1)}},
{"datatypehandle", {"DataTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DataTypeHandle", 1, 1)}},
{"optionaltypehandle", {"OptionalTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("OptionalTypeHandle", 1, 1)}},
- {"lineartypehandle", {"LinearTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearTypeHandle", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"dynamiclineartypehandle", {"DynamicLinearTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DynamicLinearTypeHandle", 1, 1), NYql::MakeLangVersion(2025, 4)}},
+ {"lineartypehandle", {"LinearTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearTypeHandle", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dynamiclineartypehandle", {"DynamicLinearTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DynamicLinearTypeHandle", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
{"listtypehandle", {"ListTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ListTypeHandle", 1, 1)}},
{"streamtypehandle", {"StreamTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("StreamTypeHandle", 1, 1)}},
{"tupletypecomponents", {"TupleTypeComponents", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("TupleTypeComponents", 1, 1)}},
@@ -3440,7 +3441,7 @@ struct TBuiltinFuncData {
{"coalesce", {"Coalesce", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Coalesce", 1, -1)}},
{"nvl", {"Nvl", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Coalesce", 1, -1)}},
{"nanvl", {"Nanvl", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Nanvl", 2, 2)}},
- {"nullif", {"NullIf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("NullIf", 2, 2), NYql::MakeLangVersion(2025, 4)}},
+ {"nullif", {"NullIf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("NullIf", 2, 2), NYql::NFeature::NullIfFunction.MinLangVer}},
{"likely", {"Likely", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Likely", 1, -1)}},
{"assumestrict", {"AssumeStrict", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AssumeStrict", 1, 1)}},
{"assumenonstrict", {"AssumeNonStrict", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AssumeNonStrict", 1, 1)}},
@@ -3600,8 +3601,8 @@ struct TBuiltinFuncData {
{"first", {"First", "MatchRec", BuildAggrFuncFactoryCallback("First", "first_traits_factory")}},
{"last", {"Last", "MatchRec", BuildAggrFuncFactoryCallback("Last", "last_traits_factory")}},
- {"randomsample", {"RandomSample", "Agg", BuildAggrFuncFactoryCallback("RandomSample", "random_sample_factory", RANDOM_SAMPLE), NYql::MakeLangVersion(2025, 4)}},
- {"randomvalue", {"RandomValue", "Agg", BuildAggrFuncFactoryCallback("RandomValue", "random_value_factory", RANDOM_VALUE), NYql::MakeLangVersion(2025, 4)}},
+ {"randomsample", {"RandomSample", "Agg", BuildAggrFuncFactoryCallback("RandomSample", "random_sample_factory", RANDOM_SAMPLE), NYql::NFeature::RandomValueSampleFunction.MinLangVer}},
+ {"randomvalue", {"RandomValue", "Agg", BuildAggrFuncFactoryCallback("RandomValue", "random_value_factory", RANDOM_VALUE), NYql::NFeature::RandomValueSampleFunction.MinLangVer}},
};
// NOLINTEND(modernize-use-designated-initializers)
return aggrFuncs;
diff --git a/yql/essentials/sql/v1/sql_ut_common.h b/yql/essentials/sql/v1/sql_ut_common.h
index 5967ed3ae17..b53675b12ea 100644
--- a/yql/essentials/sql/v1/sql_ut_common.h
+++ b/yql/essentials/sql/v1/sql_ut_common.h
@@ -13559,7 +13559,7 @@ Y_UNIT_TEST(ReadsNamedNodeExpresionSubquery) {
Y_UNIT_TEST(ReadsProjectionFromSubquery) {
NSQLTranslation::TTranslationSettings s;
- s.LangVer = NYql::MakeLangVersion(2025, 4);
+ s.LangVer = NYql::NFeature::InlineSubquery.MinLangVer;
NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql(
SELECT (SELECT a FROM plato.x) FROM (SELECT * FROM plato.y);
diff --git a/yql/essentials/tests/sql/minirun/part12/canondata/result.json b/yql/essentials/tests/sql/minirun/part12/canondata/result.json
index 42084f648c6..c4aba194e41 100644
--- a/yql/essentials/tests/sql/minirun/part12/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part12/canondata/result.json
@@ -514,14 +514,14 @@
{
"checksum": "91294751a47cfcb87a5a3af5e0db632c",
"size": 1564,
- "uri": "https://{canondata_backend}/1899731/db557cf50e84fba5e07ed252ed220f77f5fa9cc9/resource.tar.gz#test.test_expr-cast_decimal-default.txt-Debug_/opt.yql"
+ "uri": "https://{canondata_backend}/1925842/486531303c5d3ec35ed8056fbdb4be070aadc061/resource.tar.gz#test.test_expr-cast_decimal-default.txt-Debug_/opt.yql"
}
],
"test.test[expr-cast_decimal-default.txt-Results]": [
{
"checksum": "7bede4f19295253e7740f658c336afed",
"size": 14064,
- "uri": "https://{canondata_backend}/1899731/db557cf50e84fba5e07ed252ed220f77f5fa9cc9/resource.tar.gz#test.test_expr-cast_decimal-default.txt-Results_/results.txt"
+ "uri": "https://{canondata_backend}/1925842/486531303c5d3ec35ed8056fbdb4be070aadc061/resource.tar.gz#test.test_expr-cast_decimal-default.txt-Results_/results.txt"
}
],
"test.test[expr-dict_comp-default.txt-Debug]": [
diff --git a/yql/essentials/tests/sql/minirun/part13/canondata/result.json b/yql/essentials/tests/sql/minirun/part13/canondata/result.json
index 84f4f087115..82cf4f95a1c 100644
--- a/yql/essentials/tests/sql/minirun/part13/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part13/canondata/result.json
@@ -533,6 +533,20 @@
"uri": "https://{canondata_backend}/1871002/12af85b5b27eccd0e3391513f34ad8082ac84843/resource.tar.gz#test.test_expr-checked_ops-default.txt-Results_/results.txt"
}
],
+ "test.test[expr-decimal_common_type_conversions_with_fix-default.txt-Debug]": [
+ {
+ "checksum": "8bab68761984ab80e269b3d705daf582",
+ "size": 1115,
+ "uri": "https://{canondata_backend}/1942100/728bccc20a9bd07cadc3e00d0a0dc17203fcdc53/resource.tar.gz#test.test_expr-decimal_common_type_conversions_with_fix-default.txt-Debug_/opt.yql"
+ }
+ ],
+ "test.test[expr-decimal_common_type_conversions_with_fix-default.txt-Results]": [
+ {
+ "checksum": "f106eff3770c8b7cc69cf506ea3893f7",
+ "size": 2831,
+ "uri": "https://{canondata_backend}/1942100/728bccc20a9bd07cadc3e00d0a0dc17203fcdc53/resource.tar.gz#test.test_expr-decimal_common_type_conversions_with_fix-default.txt-Results_/results.txt"
+ }
+ ],
"test.test[expr-exapnd_with_singular_types-default.txt-Debug]": [
{
"checksum": "5ba3b6e81f1e0368769ed577c996d3cb",
diff --git a/yql/essentials/tests/sql/minirun/part15/canondata/result.json b/yql/essentials/tests/sql/minirun/part15/canondata/result.json
index 6ec04718f48..fe0b1cd7794 100644
--- a/yql/essentials/tests/sql/minirun/part15/canondata/result.json
+++ b/yql/essentials/tests/sql/minirun/part15/canondata/result.json
@@ -304,7 +304,7 @@
{
"checksum": "6fcabdb0c5f77c3b905a9f978ea9ca1d",
"size": 2420,
- "uri": "https://{canondata_backend}/1817427/c18651bc495886827ae93a38105dcaa87d7201dd/resource.tar.gz#test.test_combine-YQL-16277_dds_example-default.txt-Debug_/opt.yql"
+ "uri": "https://{canondata_backend}/1942525/8d0b2a987b2073b5eadfcff0b1c34362339c8005/resource.tar.gz#test.test_combine-YQL-16277_dds_example-default.txt-Debug_/opt.yql"
}
],
"test.test[combine-YQL-16277_dds_example-default.txt-Results]": [
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/result.json b/yql/essentials/tests/sql/sql2yql/canondata/result.json
index b52bb5fe2f7..c7586d62951 100644
--- a/yql/essentials/tests/sql/sql2yql/canondata/result.json
+++ b/yql/essentials/tests/sql/sql2yql/canondata/result.json
@@ -3594,7 +3594,7 @@
{
"checksum": "0beea95df85e9ea70b06ffa9d9eac69f",
"size": 4965,
- "uri": "https://{canondata_backend}/1942100/698d95eec1cbd4e4ada1a8da105c203a7a12bb85/resource.tar.gz#test_sql2yql.test_expr-cast_decimal_/sql.yql"
+ "uri": "https://{canondata_backend}/1942100/6c6381874f89e449bdc52f3bb6bfee809e1130fd/resource.tar.gz#test_sql2yql.test_expr-cast_decimal_/sql.yql"
}
],
"test_sql2yql.test[expr-cast_decimal_implicit_fail_1]": [
@@ -3744,6 +3744,13 @@
"uri": "https://{canondata_backend}/1942100/698d95eec1cbd4e4ada1a8da105c203a7a12bb85/resource.tar.gz#test_sql2yql.test_expr-decimal_bytes_/sql.yql"
}
],
+ "test_sql2yql.test[expr-decimal_common_type_conversions_with_fix]": [
+ {
+ "checksum": "e156db425a39477aab5febcd1544ceed",
+ "size": 2028,
+ "uri": "https://{canondata_backend}/1880306/b9032f639802a04f956ae89f0048f5368bd0d8f7/resource.tar.gz#test_sql2yql.test_expr-decimal_common_type_conversions_with_fix_/sql.yql"
+ }
+ ],
"test_sql2yql.test[expr-deep_graph]": [
{
"checksum": "cbe0cd19b44df85f21f6e37de4712643",
@@ -4381,6 +4388,13 @@
"uri": "https://{canondata_backend}/1942100/698d95eec1cbd4e4ada1a8da105c203a7a12bb85/resource.tar.gz#test_sql2yql.test_expr-many_opt_comp_/sql.yql"
}
],
+ "test_sql2yql.test[expr-min_of_decimal_fail]": [
+ {
+ "checksum": "7d7ab3387ea5b34e5a6294a34184ee54",
+ "size": 1216,
+ "uri": "https://{canondata_backend}/1600758/1272ec3c4db035daaae64a34bc633558e3588957/resource.tar.gz#test_sql2yql.test_expr-min_of_decimal_fail_/sql.yql"
+ }
+ ],
"test_sql2yql.test[expr-minmax_for_complex_types]": [
{
"checksum": "00ddcd017a69daa4562777c910ff33af",
@@ -15323,6 +15337,11 @@
"uri": "file://test_sql_format.test_expr-decimal_bytes_/formatted.sql"
}
],
+ "test_sql_format.test[expr-decimal_common_type_conversions_with_fix]": [
+ {
+ "uri": "file://test_sql_format.test_expr-decimal_common_type_conversions_with_fix_/formatted.sql"
+ }
+ ],
"test_sql_format.test[expr-deep_graph]": [
{
"uri": "file://test_sql_format.test_expr-deep_graph_/formatted.sql"
@@ -15778,6 +15797,11 @@
"uri": "file://test_sql_format.test_expr-many_opt_comp_/formatted.sql"
}
],
+ "test_sql_format.test[expr-min_of_decimal_fail]": [
+ {
+ "uri": "file://test_sql_format.test_expr-min_of_decimal_fail_/formatted.sql"
+ }
+ ],
"test_sql_format.test[expr-minmax_for_complex_types]": [
{
"uri": "file://test_sql_format.test_expr-minmax_for_complex_types_/formatted.sql"
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-decimal_common_type_conversions_with_fix_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-decimal_common_type_conversions_with_fix_/formatted.sql
new file mode 100644
index 00000000000..d69eec3f15f
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-decimal_common_type_conversions_with_fix_/formatted.sql
@@ -0,0 +1,8 @@
+PRAGMA config.flags('DecimalCommonTypeConversionMode', 'with_common_type_fixup');
+
+SELECT
+ AsList(CAST(1 AS Decimal (4, 3)), CAST(2 AS Decimal (5, 4))),
+ AsList(CAST(1 AS Decimal (4, 3)), CAST(2 AS Decimal (9, 2))),
+ AsList(CAST(1 AS Decimal (33, 16)), CAST(2 AS Decimal (33, 18))),
+ AsList(CAST(1 AS Decimal (30, 10)), CAST(2 AS Decimal (20, 15)))
+;
diff --git a/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-min_of_decimal_fail_/formatted.sql b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-min_of_decimal_fail_/formatted.sql
new file mode 100644
index 00000000000..69e4608321a
--- /dev/null
+++ b/yql/essentials/tests/sql/sql2yql/canondata/test_sql_format.test_expr-min_of_decimal_fail_/formatted.sql
@@ -0,0 +1,6 @@
+PRAGMA config.flags('DecimalCommonTypeConversionMode', 'with_common_type_fixup');
+
+/* custom error: Cannot infer common type for Decimal(34,16) and Decimal(33,18): common precision is 36, that is greater than 35 */
+SELECT
+ min_of(CAST(1 AS Decimal (34, 16)), CAST(2 AS Decimal (33, 18)))
+;
diff --git a/yql/essentials/tests/sql/suites/expr/decimal_common_type_conversions_with_fix.yql b/yql/essentials/tests/sql/suites/expr/decimal_common_type_conversions_with_fix.yql
new file mode 100644
index 00000000000..7d2516e3bc3
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/expr/decimal_common_type_conversions_with_fix.yql
@@ -0,0 +1,7 @@
+PRAGMA config.flags("DecimalCommonTypeConversionMode", "with_common_type_fixup");
+
+SELECT
+ AsList(CAST(1 as Decimal(4, 3)), CAST(2 as Decimal(5, 4))),
+ AsList(CAST(1 as Decimal(4, 3)), CAST(2 as Decimal(9, 2))),
+ AsList(CAST(1 as Decimal(33, 16)), CAST(2 as Decimal(33, 18))),
+ AsList(CAST(1 as Decimal(30, 10)), CAST(2 as Decimal(20, 15)));
diff --git a/yql/essentials/tests/sql/suites/expr/min_of_decimal_fail.cfg b/yql/essentials/tests/sql/suites/expr/min_of_decimal_fail.cfg
new file mode 100644
index 00000000000..5dae597903c
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/expr/min_of_decimal_fail.cfg
@@ -0,0 +1 @@
+xfail
diff --git a/yql/essentials/tests/sql/suites/expr/min_of_decimal_fail.yql b/yql/essentials/tests/sql/suites/expr/min_of_decimal_fail.yql
new file mode 100644
index 00000000000..f7034abb702
--- /dev/null
+++ b/yql/essentials/tests/sql/suites/expr/min_of_decimal_fail.yql
@@ -0,0 +1,4 @@
+PRAGMA config.flags("DecimalCommonTypeConversionMode", "with_common_type_fixup");
+
+/* custom error: Cannot infer common type for Decimal(34,16) and Decimal(33,18): common precision is 36, that is greater than 35 */
+select min_of(cast(1 as Decimal(34, 16)), cast(2 as Decimal(33, 18)))
diff --git a/yql/essentials/tools/yql_facade_run/yql_facade_run.cpp b/yql/essentials/tools/yql_facade_run/yql_facade_run.cpp
index 98b43d63c7c..6f55213a283 100644
--- a/yql/essentials/tools/yql_facade_run/yql_facade_run.cpp
+++ b/yql/essentials/tools/yql_facade_run/yql_facade_run.cpp
@@ -364,6 +364,7 @@ void TFacadeRunOptions::Parse(int argc, const char** argv) {
opts.AddLongOption("sql-flags", "SQL translator pragma flags").SplitHandler(&sqlFlags, ',');
opts.AddLongOption("syntax-version", "SQL syntax version").StoreResult(&SyntaxVersion).DefaultValue(1);
opts.AddLongOption("ansi-lexer", "Use ansi lexer").NoArgument().SetFlag(&AnsiLexer);
+ opts.AddLongOption("auto-use-yql-libs", "Implicitly mark yql_libs/* files as libraries").NoArgument().SetFlag(&AutoUseYqlLibs);
opts.AddLongOption("assume-ydb-on-slash", "Assume YDB provider if cluster name starts with '/'").NoArgument().SetFlag(&AssumeYdbOnClusterWithSlash);
opts.AddLongOption("with-final-issues", "Include some final messages (like statistic) in issues").NoArgument().SetFlag(&WithFinalIssues);
@@ -787,6 +788,10 @@ int TFacadeRunner::DoMain(int argc, const char** argv) {
factory.SetCredentials(RunOptions_.Credentials);
factory.EnableRangeComputeFor();
+ if (RunOptions_.AutoUseYqlLibs) {
+ factory.EnableAutoUseYqlLibs();
+ }
+
if (!urlListers.empty()) {
factory.SetUrlListerManager(MakeUrlListerManager(urlListers));
}
diff --git a/yql/essentials/tools/yql_facade_run/yql_facade_run.h b/yql/essentials/tools/yql_facade_run/yql_facade_run.h
index aea93600061..a960ffb3764 100644
--- a/yql/essentials/tools/yql_facade_run/yql_facade_run.h
+++ b/yql/essentials/tools/yql_facade_run/yql_facade_run.h
@@ -91,6 +91,7 @@ public:
bool AnsiLexer = false;
bool TestAntlr4 = false;
bool AssumeYdbOnClusterWithSlash = false;
+ bool AutoUseYqlLibs = false;
bool TestSqlFormat = false;
bool TestLexers = false;
bool TestComplete = false;
diff --git a/yql/essentials/udfs/common/datetime2/datetime_udf.cpp b/yql/essentials/udfs/common/datetime2/datetime_udf.cpp
index 10c10435246..50b70cc59d8 100644
--- a/yql/essentials/udfs/common/datetime2/datetime_udf.cpp
+++ b/yql/essentials/udfs/common/datetime2/datetime_udf.cpp
@@ -5,6 +5,7 @@
#include <yql/essentials/public/udf/udf_helpers.h>
#include <yql/essentials/public/udf/arrow/udf_arrow_helpers.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/public/langver/yql_langver.h>
#include <util/datetime/base.h>
@@ -2943,7 +2944,7 @@ public:
NYql::TLangVersion WriteOffsetWithColonAvailableSince;
TStringRef WriteOffsetWithColonRuntimeSetting(builder.GetRuntimeSetting(TStringRef::Of("MakeWriteOffsetWithColonAvailableSince")));
if (WriteOffsetWithColonRuntimeSetting.empty()) {
- WriteOffsetWithColonAvailableSince = NYql::MakeLangVersion(2025, 5);
+ WriteOffsetWithColonAvailableSince = NYql::NFeature::WriteOffsetWithColon.MinLangVer;
} else {
if (!NYql::ParseLangVersion(WriteOffsetWithColonRuntimeSetting, WriteOffsetWithColonAvailableSince)) {
UdfTerminate((TStringBuilder() << "Runtime setting 'MakeWriteOffsetWithColonAvailableSince' is misconfigured").c_str());
@@ -3537,7 +3538,7 @@ private:
break;
}
case 'z':
- if (currentLangVersion < NYql::MakeLangVersion(2025, 5)) {
+ if (currentLangVersion < NYql::NFeature::DateTimeFormatZ.MinLangVer) {
throw yexception() << "%z specfifier is available since 2025.05";
}
if (useTzNameScanner) {
@@ -3751,7 +3752,7 @@ SIMPLE_MODULE(TDateTime2Module,
TIntervalFromMinutes,
TLangVerForked<
- NYql::MakeLangVersion(2025, 03),
+ NYql::MakeLangVersion(2025, 03), // TODO(YQL-21408)
NLegacy::TIntervalFromSeconds,
NActual::TIntervalFromSeconds>,
@@ -3801,7 +3802,7 @@ SIMPLE_MODULE(TDateTime2Module,
SimpleDatetimeToIntervalUdf<TM64ResourceName, EndOf<TTM64Storage>>>,
TLangVerForked<
- NYql::MakeLangVersion(2025, 03),
+ NYql::MakeLangVersion(2025, 03), // TODO(YQL-21408)
TToUnits<ToSecondsUDF, /* TResult = */ ui32, /* TSignedResult = */ i32, /* TWResult = */ i64, 1>,
TToUnits<ToSecondsUDF, /* TResult = */ ui32, /* TSignedResult = */ i64, /* TWResult = */ i64, 1>>,
diff --git a/yql/essentials/udfs/common/datetime2/ya.make b/yql/essentials/udfs/common/datetime2/ya.make
index 1ce91e1a683..2eb3cbae9dd 100644
--- a/yql/essentials/udfs/common/datetime2/ya.make
+++ b/yql/essentials/udfs/common/datetime2/ya.make
@@ -13,6 +13,7 @@ YQL_UDF_CONTRIB(datetime2_udf)
library/cpp/type_info/tz
yql/essentials/public/udf/arrow
yql/essentials/public/langver
+ yql/essentials/core/langver
yql/essentials/minikql
yql/essentials/minikql/datetime
)
diff --git a/yql/essentials/udfs/common/digest/digest_udf.cpp b/yql/essentials/udfs/common/digest/digest_udf.cpp
index 8a9ae8337ea..f8cfd3952d0 100644
--- a/yql/essentials/udfs/common/digest/digest_udf.cpp
+++ b/yql/essentials/udfs/common/digest/digest_udf.cpp
@@ -1,5 +1,6 @@
#include <yql/essentials/public/udf/udf_helpers.h>
#include <yql/essentials/public/udf/udf_value_builder.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/public/langver/yql_langver.h>
#include <util/digest/murmur.h>
@@ -394,7 +395,7 @@ SIMPLE_STRICT_UDF(TSha256, char*(TAutoMap<char*>)) {
return valueBuilder->NewString(TStringRef(reinterpret_cast<char*>(hash.data()), sizeof(hash)));
}
-SIMPLE_STRICT_UDF_OPTIONS(TSha512, char*(TAutoMap<char*>), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 3));) {
+SIMPLE_STRICT_UDF_OPTIONS(TSha512, char*(TAutoMap<char*>), builder.SetMinLangVer(NYql::NFeature::Sha512.MinLangVer);) {
const auto& inputRef = args[0].AsStringRef();
SHA512_CTX sha;
SHA512_Init(&sha);
diff --git a/yql/essentials/udfs/common/digest/ya.make b/yql/essentials/udfs/common/digest/ya.make
index 565e77a3013..2a01a3cc946 100644
--- a/yql/essentials/udfs/common/digest/ya.make
+++ b/yql/essentials/udfs/common/digest/ya.make
@@ -11,6 +11,7 @@ YQL_UDF_CONTRIB(digest_udf)
)
PEERDIR(
+ yql/essentials/core/langver
contrib/libs/farmhash
contrib/libs/highwayhash
contrib/libs/openssl
diff --git a/yql/essentials/udfs/common/ip_base/lib/ip_base_udf.h b/yql/essentials/udfs/common/ip_base/lib/ip_base_udf.h
index bcba7811b88..b2d34239dcc 100644
--- a/yql/essentials/udfs/common/ip_base/lib/ip_base_udf.h
+++ b/yql/essentials/udfs/common/ip_base/lib/ip_base_udf.h
@@ -1,5 +1,6 @@
#pragma once
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/public/udf/udf_helpers.h>
#include <yql/essentials/public/langver/yql_langver.h>
@@ -232,7 +233,7 @@ SIMPLE_STRICT_UDF(TFromString, TOptionalString(TAutoMapString)) {
return valueBuilder->NewString(SerializeAddress(addr));
}
-SIMPLE_STRICT_UDF_OPTIONS(TIpv4FromUint32, char*(TAutoMapUint32), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 3))) {
+SIMPLE_STRICT_UDF_OPTIONS(TIpv4FromUint32, char*(TAutoMapUint32), builder.SetMinLangVer(NYql::NFeature::IPv4Uint32.MinLangVer)) {
// in_addr expects bytes in network byte order.
in_addr addr;
addr.s_addr = htonl(args[0].Get<ui32>());
@@ -249,7 +250,7 @@ SIMPLE_UDF(TToString, char*(TAutoMapString)) {
return valueBuilder->NewString(DeserializeAddress(args[0].AsStringRef()).ToString(/*PrintScopeId=*/false));
}
-SIMPLE_UDF_OPTIONS(TIpv4ToUint32, TOptionalUint32(TAutoMapString), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 3))) {
+SIMPLE_UDF_OPTIONS(TIpv4ToUint32, TOptionalUint32(TAutoMapString), builder.SetMinLangVer(NYql::NFeature::IPv4Uint32.MinLangVer)) {
Y_UNUSED(valueBuilder);
TIpv6Address addr = DeserializeAddress(args[0].AsStringRef());
if (addr.Type() != TIpv6Address::Ipv4) {
diff --git a/yql/essentials/udfs/common/ip_base/lib/ya.make b/yql/essentials/udfs/common/ip_base/lib/ya.make
index ab9b2bce8e2..53754024ff9 100644
--- a/yql/essentials/udfs/common/ip_base/lib/ya.make
+++ b/yql/essentials/udfs/common/ip_base/lib/ya.make
@@ -12,6 +12,7 @@ SRCS(
PEERDIR(
yql/essentials/public/udf
+ yql/essentials/core/langver
library/cpp/ipmath
library/cpp/ipv6_address
)
diff --git a/yql/essentials/udfs/common/math/math_udf.cpp b/yql/essentials/udfs/common/math/math_udf.cpp
index 6b3e8409e84..d647a75656e 100644
--- a/yql/essentials/udfs/common/math/math_udf.cpp
+++ b/yql/essentials/udfs/common/math/math_udf.cpp
@@ -1,6 +1,7 @@
#include "math_ir.h"
#include <util/system/byteorder.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/public/langver/yql_langver.h>
#include <yql/essentials/public/udf/udf_helpers.h>
@@ -122,7 +123,7 @@ public:
builder.SimpleSignature<TUserType(TAutoMap<TUserType>)>()
.IsStrict()
- .SetMinLangVer(NYql::MakeLangVersion(2025, 3));
+ .SetMinLangVer(NYql::NFeature::SwapBytes.MinLangVer);
if (!typesOnly) {
builder.Implementation(new TSwapBytesFunc<TUserType>(builder.GetSourcePosition()));
}
diff --git a/yql/essentials/udfs/common/math/ya.make b/yql/essentials/udfs/common/math/ya.make
index 25e7b6d28fb..10c012dfacf 100644
--- a/yql/essentials/udfs/common/math/ya.make
+++ b/yql/essentials/udfs/common/math/ya.make
@@ -74,6 +74,7 @@ YQL_UDF_CONTRIB(math_udf)
ENDIF()
PEERDIR(
+ yql/essentials/core/langver
yql/essentials/udfs/common/math/lib
)
diff --git a/yql/essentials/udfs/common/re2/re2_udf.cpp b/yql/essentials/udfs/common/re2/re2_udf.cpp
index 5444b7a731f..4ea239c0d00 100644
--- a/yql/essentials/udfs/common/re2/re2_udf.cpp
+++ b/yql/essentials/udfs/common/re2/re2_udf.cpp
@@ -1,3 +1,4 @@
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/public/langver/yql_langver.h>
#include <yql/essentials/public/udf/udf_helpers.h>
#include <yql/essentials/public/udf/udf_type_ops.h>
@@ -53,7 +54,7 @@ ui64 GetFailProbability() {
}
bool ShouldFailOnInvalidRegexp(const std::string_view regexp, NYql::TLangVersion currentLangVersion) {
- if (currentLangVersion >= NYql::MakeLangVersion(2025, 3)) {
+ if (currentLangVersion >= NYql::NFeature::ValidateRegexp.MinLangVer) {
return true;
}
THashType hash = GetStringHash(regexp) % 100;
diff --git a/yql/essentials/udfs/common/re2/ya.make b/yql/essentials/udfs/common/re2/ya.make
index ca05ede88f7..daf0664d87d 100644
--- a/yql/essentials/udfs/common/re2/ya.make
+++ b/yql/essentials/udfs/common/re2/ya.make
@@ -1,5 +1,5 @@
YQL_UDF_CONTRIB(re2_udf)
-
+
YQL_ABI_VERSION(
2
46
@@ -9,12 +9,13 @@ YQL_UDF_CONTRIB(re2_udf)
SRCS(
re2_udf.cpp
)
-
+
PEERDIR(
+ yql/essentials/core/langver
contrib/libs/re2
library/cpp/deprecated/enum_codegen
)
-
+
END()
RECURSE_FOR_TESTS(
diff --git a/yql/essentials/udfs/common/reservoir_sampling/reservoir_udf.cpp b/yql/essentials/udfs/common/reservoir_sampling/reservoir_udf.cpp
index 91d43de0dbd..e511bba3c15 100644
--- a/yql/essentials/udfs/common/reservoir_sampling/reservoir_udf.cpp
+++ b/yql/essentials/udfs/common/reservoir_sampling/reservoir_udf.cpp
@@ -3,6 +3,7 @@
#include <yql/essentials/public/udf/udf_helpers.h>
#include <yql/essentials/public/udf/udf_type_ops.h>
#include <yql/essentials/public/langver/yql_langver.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <util/generic/vector.h>
#include <util/random/random.h>
@@ -268,7 +269,7 @@ public:
ui32 flags,
IFunctionTypeInfoBuilder& builder) const final
{
- builder.SetMinLangVer(NYql::MakeLangVersion(2025, 4));
+ builder.SetMinLangVer(NYql::NFeature::RandomValueSampleFunction.MinLangVer);
try {
bool typesOnly = (flags & TFlags::TypesOnly);
builder.UserType(userType);
diff --git a/yql/essentials/udfs/common/reservoir_sampling/ya.make b/yql/essentials/udfs/common/reservoir_sampling/ya.make
index 2a609a0eb79..7df8f130936 100644
--- a/yql/essentials/udfs/common/reservoir_sampling/ya.make
+++ b/yql/essentials/udfs/common/reservoir_sampling/ya.make
@@ -1,5 +1,5 @@
YQL_UDF(reservoir_sampling_udf)
-
+
YQL_ABI_VERSION(
2
43
@@ -11,6 +11,7 @@ YQL_UDF(reservoir_sampling_udf)
)
PEERDIR(
+ yql/essentials/core/langver
yql/essentials/udfs/common/reservoir_sampling/lib
)
diff --git a/yql/essentials/udfs/common/string/string_udf.cpp b/yql/essentials/udfs/common/string/string_udf.cpp
index 08781ce98ee..b89bcf3b84f 100644
--- a/yql/essentials/udfs/common/string/string_udf.cpp
+++ b/yql/essentials/udfs/common/string/string_udf.cpp
@@ -1,6 +1,7 @@
#include <yql/essentials/public/udf/udf_allocator.h>
#include <yql/essentials/public/udf/udf_helpers.h>
#include <yql/essentials/public/udf/udf_value_builder.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/public/langver/yql_langver.h>
#include <library/cpp/deprecated/split/split_iterator.h>
@@ -118,7 +119,7 @@ TString ReverseBits(const TStringRef input) {
// NOTE: The functions below are marked as deprecated, so block implementation
// is not required for them
SIMPLE_STRICT_UDF_OPTIONS(TReverse, TOptional<char*>(TOptional<char*>),
- builder.SetMaxLangVer(NYql::MakeLangVersion(2025, 1))) {
+ builder.SetMaxLangVer(NYql::NFeature::StringReverse.MaxLangVer)) {
EMPTY_RESULT_ON_EMPTY_ARG(0)
const TStringBuf input(args[0].AsStringRef());
try {
@@ -180,17 +181,17 @@ SIMPLE_STRICT_UDF_OPTIONS(TReverse, TOptional<char*>(TOptional<char*>),
} \
}
-#define STRING_TWO_ARGS_UDF_DEPRECATED_2025_02(udfName, function) \
- SIMPLE_STRICT_UDF_OPTIONS(T##udfName, bool(TOptional<char*>, char*), \
- builder.SetMaxLangVer(NYql::MakeLangVersion(2025, 1))) { \
- Y_UNUSED(valueBuilder); \
- if (args[0]) { \
- const TStringBuf haystack(args[0].AsStringRef()); \
- const TStringBuf needle(args[1].AsStringRef()); \
- return TUnboxedValuePod(function(haystack, needle)); \
- } else { \
- return TUnboxedValuePod(false); \
- } \
+#define STRING_TWO_ARGS_UDF_DEPRECATED_2025_02(udfName, function) \
+ SIMPLE_STRICT_UDF_OPTIONS(T##udfName, bool(TOptional<char*>, char*), \
+ builder.SetMaxLangVer(NYql::NFeature::StringTwoArgs.MaxLangVer)) { \
+ Y_UNUSED(valueBuilder); \
+ if (args[0]) { \
+ const TStringBuf haystack(args[0].AsStringRef()); \
+ const TStringBuf needle(args[1].AsStringRef()); \
+ return TUnboxedValuePod(function(haystack, needle)); \
+ } else { \
+ return TUnboxedValuePod(false); \
+ } \
}
#define STRING_ASCII_CMP_IGNORE_CASE_UDF(udfName, function, minVersion) \
@@ -382,21 +383,21 @@ SIMPLE_STRICT_UDF_OPTIONS(TReverse, TOptional<char*>(TOptional<char*>),
\
END_SIMPLE_ARROW_UDF(T##udfName, T##udfName##KernelExec::Do)
-#define STRING_UDF_MAP(XX) \
- XX(Base32Encode, Base32Encode, NYql::UnknownLangVersion) \
- XX(Base64Encode, Base64Encode, NYql::UnknownLangVersion) \
- XX(Base64EncodeUrl, Base64EncodeUrl, NYql::UnknownLangVersion) \
- XX(EscapeC, EscapeC, NYql::UnknownLangVersion) \
- XX(UnescapeC, UnescapeC, NYql::UnknownLangVersion) \
- XX(HexEncode, HexEncode, NYql::UnknownLangVersion) \
- XX(EncodeHtml, EncodeHtmlPcdata, NYql::UnknownLangVersion) \
- XX(DecodeHtml, DecodeHtmlPcdata, NYql::UnknownLangVersion) \
- XX(CgiEscape, CGIEscapeRet, NYql::UnknownLangVersion) \
- XX(CgiUnescape, CGIUnescapeRet, NYql::UnknownLangVersion) \
- XX(Strip, StripString, NYql::UnknownLangVersion) \
- XX(Collapse, Collapse, NYql::UnknownLangVersion) \
- XX(ReverseBytes, ReverseBytes, NYql::MakeLangVersion(2025, 2)) \
- XX(ReverseBits, ReverseBits, NYql::MakeLangVersion(2025, 2))
+#define STRING_UDF_MAP(XX) \
+ XX(Base32Encode, Base32Encode, NYql::UnknownLangVersion) \
+ XX(Base64Encode, Base64Encode, NYql::UnknownLangVersion) \
+ XX(Base64EncodeUrl, Base64EncodeUrl, NYql::UnknownLangVersion) \
+ XX(EscapeC, EscapeC, NYql::UnknownLangVersion) \
+ XX(UnescapeC, UnescapeC, NYql::UnknownLangVersion) \
+ XX(HexEncode, HexEncode, NYql::UnknownLangVersion) \
+ XX(EncodeHtml, EncodeHtmlPcdata, NYql::UnknownLangVersion) \
+ XX(DecodeHtml, DecodeHtmlPcdata, NYql::UnknownLangVersion) \
+ XX(CgiEscape, CGIEscapeRet, NYql::UnknownLangVersion) \
+ XX(CgiUnescape, CGIUnescapeRet, NYql::UnknownLangVersion) \
+ XX(Strip, StripString, NYql::UnknownLangVersion) \
+ XX(Collapse, Collapse, NYql::UnknownLangVersion) \
+ XX(ReverseBytes, ReverseBytes, NYql::NFeature::StringReverseBytes.MinLangVer) \
+ XX(ReverseBits, ReverseBits, NYql::NFeature::StringReverseBytes.MinLangVer)
#define STRING_UNSAFE_UDF_MAP(XX) \
XX(Base32Decode, Base32Decode) \
@@ -436,10 +437,10 @@ SIMPLE_STRICT_UDF_OPTIONS(TReverse, TOptional<char*>(TOptional<char*>),
XX(HasPrefixIgnoreCase, AsciiHasPrefixIgnoreCase) \
XX(HasSuffixIgnoreCase, AsciiHasSuffixIgnoreCase)
-#define STRING_ASCII_CMP_IGNORE_CASE_UDF_MAP(XX) \
- XX(AsciiStartsWithIgnoreCase, AsciiHasPrefixIgnoreCase, NYql::MakeLangVersion(2025, 1)) \
- XX(AsciiEndsWithIgnoreCase, AsciiHasSuffixIgnoreCase, NYql::MakeLangVersion(2025, 1)) \
- XX(AsciiEqualsIgnoreCase, AsciiEqualsIgnoreCase, NYql::MakeLangVersion(2025, 2))
+#define STRING_ASCII_CMP_IGNORE_CASE_UDF_MAP(XX) \
+ XX(AsciiStartsWithIgnoreCase, AsciiHasPrefixIgnoreCase, NYql::NFeature::StringAsciiPrefixSuffixIgnoreCase.MinLangVer) \
+ XX(AsciiEndsWithIgnoreCase, AsciiHasSuffixIgnoreCase, NYql::NFeature::StringAsciiPrefixSuffixIgnoreCase.MinLangVer) \
+ XX(AsciiEqualsIgnoreCase, AsciiEqualsIgnoreCase, NYql::NFeature::StringAsciiEqualsContainsIgnoreCase.MinLangVer)
// NOTE: The functions below are marked as deprecated, so block implementation
// is not required for them. Hence, STROKA_UDF provides only the scalar one at
@@ -560,7 +561,7 @@ TUnboxedValuePod AsciiContainsIgnoreCaseImpl(const TUnboxedValuePod* args) {
}
BEGIN_SIMPLE_STRICT_ARROW_UDF_OPTIONS(TAsciiContainsIgnoreCase, bool(TOptional<char*>, char*),
- builder.SetMinLangVer(NYql::MakeLangVersion(2025, 2)))
+ builder.SetMinLangVer(NYql::NFeature::StringAsciiEqualsContainsIgnoreCase.MinLangVer))
{
Y_UNUSED(valueBuilder);
return AsciiContainsIgnoreCaseImpl(args);
diff --git a/yql/essentials/udfs/common/string/ya.make b/yql/essentials/udfs/common/string/ya.make
index cfea8c71d15..de708d2ca47 100644
--- a/yql/essentials/udfs/common/string/ya.make
+++ b/yql/essentials/udfs/common/string/ya.make
@@ -1,5 +1,5 @@
YQL_UDF_CONTRIB(string_udf)
-
+
YQL_ABI_VERSION(
2
43
@@ -9,8 +9,9 @@ YQL_UDF_CONTRIB(string_udf)
SRCS(
string_udf.cpp
)
-
+
PEERDIR(
+ yql/essentials/core/langver
yql/essentials/public/langver
yql/essentials/public/udf/arrow
library/cpp/deprecated/split
@@ -20,11 +21,9 @@ YQL_UDF_CONTRIB(string_udf)
library/cpp/string_utils/levenshtein_diff
library/cpp/string_utils/quote
)
-
+
END()
RECURSE_FOR_TESTS(
test
)
-
-
diff --git a/yql/essentials/udfs/common/yson2/ya.make b/yql/essentials/udfs/common/yson2/ya.make
index 80fe1877dc6..072f54a73d4 100644
--- a/yql/essentials/udfs/common/yson2/ya.make
+++ b/yql/essentials/udfs/common/yson2/ya.make
@@ -1,5 +1,5 @@
YQL_UDF_CONTRIB(yson2_udf)
-
+
YQL_ABI_VERSION(
2
46
@@ -9,17 +9,17 @@ YQL_UDF_CONTRIB(yson2_udf)
SRCS(
yson2_udf.cpp
)
-
+
PEERDIR(
library/cpp/containers/stack_vector
library/cpp/yson_pull
yql/essentials/minikql/dom
yql/essentials/public/langver
+ yql/essentials/core/langver
)
-
+
END()
RECURSE_FOR_TESTS(
test
)
-
diff --git a/yql/essentials/udfs/common/yson2/yson2_udf.cpp b/yql/essentials/udfs/common/yson2/yson2_udf.cpp
index 95d67b1656b..3cbf44dd6fb 100644
--- a/yql/essentials/udfs/common/yson2/yson2_udf.cpp
+++ b/yql/essentials/udfs/common/yson2/yson2_udf.cpp
@@ -8,6 +8,7 @@
#include <yql/essentials/public/udf/udf_helpers.h>
#include <yql/essentials/public/udf/udf_type_printer.h>
#include <yql/essentials/public/langver/yql_langver.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <library/cpp/yson_pull/exceptions.h>
@@ -791,7 +792,7 @@ SIMPLE_STRICT_UDF(TIsString, bool(TAutoMap<TNodeResource>)) {
return IsTypeImpl<ENodeType::String>(*args);
}
-SIMPLE_UDF_OPTIONS(TIsUtf8, bool(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2026, 1));) {
+SIMPLE_UDF_OPTIONS(TIsUtf8, bool(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonUtf8.MinLangVer);) {
Y_UNUSED(valueBuilder);
auto x = *args;
if (IsNodeType<ENodeType::Attr>(x)) {
@@ -1610,7 +1611,7 @@ public:
}
builder.IsStrict();
- builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));
+ builder.SetMinLangVer(NYql::NFeature::YsonIterate.MinLangVer);
return true;
}
@@ -1618,79 +1619,79 @@ private:
const TFields Fields_;
};
-SIMPLE_UDF_OPTIONS(TAsBool, bool(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TAsBool, bool(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsScalar<ENodeType::Bool, true>(args[0], "boolean");
}
-SIMPLE_STRICT_UDF_OPTIONS(TTryAsBool, TOptional<bool>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_STRICT_UDF_OPTIONS(TTryAsBool, TOptional<bool>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsScalar<ENodeType::Bool, false>(args[0], "boolean");
}
-SIMPLE_UDF_OPTIONS(TAsInt64, i64(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TAsInt64, i64(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsScalar<ENodeType::Int64, true>(args[0], "int64");
}
-SIMPLE_STRICT_UDF_OPTIONS(TTryAsInt64, TOptional<i64>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_STRICT_UDF_OPTIONS(TTryAsInt64, TOptional<i64>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsScalar<ENodeType::Int64, false>(args[0], "int64");
}
-SIMPLE_UDF_OPTIONS(TAsUint64, ui64(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TAsUint64, ui64(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsScalar<ENodeType::Uint64, true>(args[0], "uint64");
}
-SIMPLE_STRICT_UDF_OPTIONS(TTryAsUint64, TOptional<ui64>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_STRICT_UDF_OPTIONS(TTryAsUint64, TOptional<ui64>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsScalar<ENodeType::Uint64, false>(args[0], "uint64");
}
-SIMPLE_UDF_OPTIONS(TAsDouble, double(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TAsDouble, double(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsScalar<ENodeType::Double, true>(args[0], "double");
}
-SIMPLE_STRICT_UDF_OPTIONS(TTryAsDouble, TOptional<double>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_STRICT_UDF_OPTIONS(TTryAsDouble, TOptional<double>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsScalar<ENodeType::Double, false>(args[0], "double");
}
-SIMPLE_UDF_OPTIONS(TAsString, char*(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TAsString, char*(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsString<true>(args[0]);
}
-SIMPLE_STRICT_UDF_OPTIONS(TTryAsString, TOptional<char*>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_STRICT_UDF_OPTIONS(TTryAsString, TOptional<char*>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsString<false>(args[0]);
}
-SIMPLE_UDF_OPTIONS(TAsUtf8, TUtf8(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::MakeLangVersion(2026, 1));) {
+SIMPLE_UDF_OPTIONS(TAsUtf8, TUtf8(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::NFeature::YsonUtf8.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsUtf8<true>(args[0]);
}
-SIMPLE_STRICT_UDF_OPTIONS(TTryAsUtf8, TOptional<TUtf8>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2026, 1));) {
+SIMPLE_STRICT_UDF_OPTIONS(TTryAsUtf8, TOptional<TUtf8>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonUtf8.MinLangVer);) {
Y_UNUSED(valueBuilder);
return AsUtf8<false>(args[0]);
}
-SIMPLE_UDF_OPTIONS(TAsList, TListType<TNodeResource>(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TAsList, TListType<TNodeResource>(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
return AsList<true>(args[0], valueBuilder);
}
-SIMPLE_STRICT_UDF_OPTIONS(TTryAsList, TOptional<TListType<TNodeResource>>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_STRICT_UDF_OPTIONS(TTryAsList, TOptional<TListType<TNodeResource>>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
return AsList<false>(args[0], valueBuilder);
}
-SIMPLE_UDF_OPTIONS(TAsDict, TDictType(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TAsDict, TDictType(TAutoMap<TNodeResource>), builder.SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
return AsDict<true>(args[0], valueBuilder);
}
-SIMPLE_STRICT_UDF_OPTIONS(TTryAsDict, TOptional<TDictType>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_STRICT_UDF_OPTIONS(TTryAsDict, TOptional<TDictType>(TAutoMap<TNodeResource>), builder.IsStrict().SetMinLangVer(NYql::NFeature::YsonConvert.MinLangVer);) {
return AsDict<false>(args[0], valueBuilder);
}
@@ -2081,64 +2082,64 @@ private:
TVector<TPathSegment, TStdAllocatorForUdf<TPathSegment>> Stack_;
};
-SIMPLE_UDF_OPTIONS(TMutCreate, TMutNodeLinear(), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutCreate, TMutNodeLinear(), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(args);
Y_UNUSED(valueBuilder);
return TUnboxedValuePod(new TMutNodeBuilder());
}
-SIMPLE_UDF_OPTIONS(TMutate, TMutNodeLinear(TNodeResource), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutate, TMutNodeLinear(TNodeResource), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
return TUnboxedValuePod(new TMutNodeBuilder(args[0]));
}
-SIMPLE_UDF_OPTIONS(TMutFreeze, TNodeResource(TMutNodeLinear), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutFreeze, TNodeResource(TMutNodeLinear), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
return TMutNodeBuilder::From(args[0]).Freeze(valueBuilder);
}
-SIMPLE_UDF_OPTIONS(TMutUpsert, TMutNodeLinear(TMutNodeLinear, TNodeResource), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutUpsert, TMutNodeLinear(TMutNodeLinear, TNodeResource), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
TMutNodeBuilder::From(args[0]).Upsert(args[1]);
return args[0];
}
-SIMPLE_UDF_OPTIONS(TMutInsert, TMutNodeLinear(TMutNodeLinear, TNodeResource), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutInsert, TMutNodeLinear(TMutNodeLinear, TNodeResource), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
TMutNodeBuilder::From(args[0]).Insert(args[1]);
return args[0];
}
-SIMPLE_UDF_OPTIONS(TMutUpdate, TMutNodeLinear(TMutNodeLinear, TNodeResource), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutUpdate, TMutNodeLinear(TMutNodeLinear, TNodeResource), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
TMutNodeBuilder::From(args[0]).Update(args[1]);
return args[0];
}
-SIMPLE_UDF_OPTIONS(TMutRemove, TMutNodeLinear(TMutNodeLinear), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutRemove, TMutNodeLinear(TMutNodeLinear), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
TMutNodeBuilder::From(args[0]).Remove();
return args[0];
}
-SIMPLE_UDF_OPTIONS(TMutRewind, TMutNodeLinear(TMutNodeLinear), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutRewind, TMutNodeLinear(TMutNodeLinear), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
TMutNodeBuilder::From(args[0]).Rewind();
return args[0];
}
-SIMPLE_UDF_OPTIONS(TMutUp, TMutNodeLinear(TMutNodeLinear), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutUp, TMutNodeLinear(TMutNodeLinear), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
TMutNodeBuilder::From(args[0]).Up();
return args[0];
}
-SIMPLE_UDF_OPTIONS(TMutDownOrCreate, TMutNodeLinear(TMutNodeLinear, const char*), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutDownOrCreate, TMutNodeLinear(TMutNodeLinear, const char*), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
TMutNodeBuilder::From(args[0]).Down(args[1], /*createIfNotExists=*/true);
return args[0];
}
-SIMPLE_UDF_OPTIONS(TMutDown, TMutNodeLinear(TMutNodeLinear, const char*), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutDown, TMutNodeLinear(TMutNodeLinear, const char*), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
auto err = TMutNodeBuilder::From(args[0]).Down(args[1], /*createIfNotExists=*/false);
if (err) {
@@ -2149,7 +2150,7 @@ SIMPLE_UDF_OPTIONS(TMutDown, TMutNodeLinear(TMutNodeLinear, const char*), builde
}
using TMutTryDownReturn = TTuple<TMutNodeLinear, bool>;
-SIMPLE_UDF_OPTIONS(TMutTryDown, TMutTryDownReturn(TMutNodeLinear, const char*), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutTryDown, TMutTryDownReturn(TMutNodeLinear, const char*), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
Y_UNUSED(valueBuilder);
auto err = TMutNodeBuilder::From(args[0]).Down(args[1], /*createIfNotExists=*/false);
TUnboxedValue* items;
@@ -2160,7 +2161,7 @@ SIMPLE_UDF_OPTIONS(TMutTryDown, TMutTryDownReturn(TMutNodeLinear, const char*),
}
using TMutExistsReturn = TTuple<TMutNodeLinear, bool>;
-SIMPLE_UDF_OPTIONS(TMutExists, TMutExistsReturn(TMutNodeLinear), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutExists, TMutExistsReturn(TMutNodeLinear), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
bool exists = TMutNodeBuilder::From(args[0]).Exists();
TUnboxedValue* items;
auto ret = valueBuilder->NewArray(2, items);
@@ -2170,7 +2171,7 @@ SIMPLE_UDF_OPTIONS(TMutExists, TMutExistsReturn(TMutNodeLinear), builder.SetMinL
}
using TMutViewReturn = TTuple<TMutNodeLinear, TOptional<TNodeResource>>;
-SIMPLE_UDF_OPTIONS(TMutView, TMutViewReturn(TMutNodeLinear), builder.SetMinLangVer(NYql::MakeLangVersion(2025, 5));) {
+SIMPLE_UDF_OPTIONS(TMutView, TMutViewReturn(TMutNodeLinear), builder.SetMinLangVer(NYql::NFeature::YsonMut.MinLangVer);) {
auto view = TMutNodeBuilder::From(args[0]).View(valueBuilder);
TUnboxedValue* items;
auto ret = valueBuilder->NewArray(2, items);
diff --git a/yt/yql/providers/yt/opt/yql_yt_join.cpp b/yt/yql/providers/yt/opt/yql_yt_join.cpp
index 93eeb49444b..4c733ca91a5 100644
--- a/yt/yql/providers/yt/opt/yql_yt_join.cpp
+++ b/yt/yql/providers/yt/opt/yql_yt_join.cpp
@@ -147,9 +147,9 @@ const TStructExprType* MakeOutputJoinColumns(const THashMap<TString, const TType
return ctx.MakeType<TStructExprType>(resultFields);
}
-const TTypeAnnotationNode* UnifyJoinKeyType(TPositionHandle pos, const TVector<const TTypeAnnotationNode*>& types, TExprContext& ctx) {
+const TTypeAnnotationNode* UnifyJoinKeyType(TPositionHandle pos, const TVector<const TTypeAnnotationNode*>& types, TExprContext& ctx, const TTypeAnnotationContext& typesCtx) {
TTypeAnnotationNode::TListType t = types;
- const TTypeAnnotationNode* commonType = CommonType(pos, t, ctx);
+ const TTypeAnnotationNode* commonType = CommonType(pos, t, ctx, typesCtx);
if (commonType && !commonType->IsOptionalOrNull()) {
NUdf::TCastResultOptions options = 0;
for (auto type : types) {
@@ -165,13 +165,13 @@ const TTypeAnnotationNode* UnifyJoinKeyType(TPositionHandle pos, const TVector<c
}
TVector<const TTypeAnnotationNode*> UnifyJoinKeyType(TPositionHandle pos, const TVector<const TTypeAnnotationNode*>& left,
- const TVector<const TTypeAnnotationNode*>& right, TExprContext& ctx)
+ const TVector<const TTypeAnnotationNode*>& right, TExprContext& ctx, const TTypeAnnotationContext& typesCtx)
{
YQL_ENSURE(left.size() == right.size());
TVector<const TTypeAnnotationNode*> ret;
ret.reserve(left.size());
for (size_t i = 0; i < left.size(); ++i) {
- ret.push_back(UnifyJoinKeyType(pos, { left[i], right[i] }, ctx));
+ ret.push_back(UnifyJoinKeyType(pos, { left[i], right[i] }, ctx, typesCtx));
}
return ret;
diff --git a/yt/yql/providers/yt/opt/yql_yt_join.h b/yt/yql/providers/yt/opt/yql_yt_join.h
index fa5b057afbe..390905666c1 100644
--- a/yt/yql/providers/yt/opt/yql_yt_join.h
+++ b/yt/yql/providers/yt/opt/yql_yt_join.h
@@ -55,9 +55,9 @@ const TTypeAnnotationNode* AsDictKeyType(const TVector<const TTypeAnnotationNode
void SwapJoinType(TPositionHandle pos, TExprNode::TPtr& joinType, TExprContext& ctx);
const TStructExprType* MakeOutputJoinColumns(const THashMap<TString, const TTypeAnnotationNode*>& columnTypes,
const TJoinLabel& label, TExprContext& ctx);
-const TTypeAnnotationNode* UnifyJoinKeyType(TPositionHandle pos, const TVector<const TTypeAnnotationNode*>& types, TExprContext& ctx);
+const TTypeAnnotationNode* UnifyJoinKeyType(TPositionHandle pos, const TVector<const TTypeAnnotationNode*>& types, TExprContext& ctx, const TTypeAnnotationContext& typesCtx);
TVector<const TTypeAnnotationNode*> UnifyJoinKeyType(TPositionHandle pos, const TVector<const TTypeAnnotationNode*>& left,
- const TVector<const TTypeAnnotationNode*>& right, TExprContext& ctx);
+ const TVector<const TTypeAnnotationNode*>& right, TExprContext& ctx, const TTypeAnnotationContext& typesCtx);
TExprNode::TPtr RemapNonConvertibleItems(const TExprNode::TPtr& input, const TJoinLabel& label,
const TExprNode& keys, const TVector<const TTypeAnnotationNode*>& unifiedKeyTypes,
TExprNode::TListType& columnNodes, TExprNode::TListType& columnNodesForSkipNull, TExprContext& ctx);
diff --git a/yt/yql/providers/yt/provider/yql_yt_datasink_type_ann.cpp b/yt/yql/providers/yt/provider/yql_yt_datasink_type_ann.cpp
index a0845dbba16..c382e340290 100644
--- a/yt/yql/providers/yt/provider/yql_yt_datasink_type_ann.cpp
+++ b/yt/yql/providers/yt/provider/yql_yt_datasink_type_ann.cpp
@@ -2390,7 +2390,7 @@ private:
const TStructExprType* resultType = nullptr;
status = EquiJoinAnnotation(input->Pos(), resultType, labels,
- *input->Child(TYtEquiJoin::idx_Joins), joinOptions, ctx);
+ *input->Child(TYtEquiJoin::idx_Joins), joinOptions, ctx, *State_->Types);
if (status != TStatus::Ok) {
return status;
}
diff --git a/yt/yql/providers/yt/provider/yql_yt_join_impl.cpp b/yt/yql/providers/yt/provider/yql_yt_join_impl.cpp
index 32fe1ccf3d8..9fdade968b9 100644
--- a/yt/yql/providers/yt/provider/yql_yt_join_impl.cpp
+++ b/yt/yql/providers/yt/provider/yql_yt_join_impl.cpp
@@ -1169,7 +1169,7 @@ bool RewriteYtMergeJoin(TYtEquiJoin equiJoin, const TJoinLabels& labels, TYtJoin
return true;
}
- auto outputKeyType = UnifyJoinKeyType(pos, inputKeyTypeLeft, inputKeyTypeRight, ctx);
+ auto outputKeyType = UnifyJoinKeyType(pos, inputKeyTypeLeft, inputKeyTypeRight, ctx, *state->Types);
TExprNode::TListType leftMembersNodes;
TExprNode::TListType rightMembersNodes;
@@ -1757,7 +1757,7 @@ bool RewriteYtMapJoin(TYtEquiJoin equiJoin, const TJoinLabels& labels, bool isLo
auto inputKeyTypeLeft = BuildJoinKeyType(mainLabel, *leftKeyColumns);
auto inputKeyTypeRight = BuildJoinKeyType(smallLabel, *rightKeyColumns);
- auto outputKeyType = UnifyJoinKeyType(pos, inputKeyTypeLeft, inputKeyTypeRight, ctx);
+ auto outputKeyType = UnifyJoinKeyType(pos, inputKeyTypeLeft, inputKeyTypeRight, ctx, *state->Types);
TMap<TStringBuf, TVector<TStringBuf>> renameMap;
if (!op.Parent) {
@@ -2605,7 +2605,7 @@ bool RewriteYtCommonJoin(TYtEquiJoin equiJoin, const TJoinLabels& labels, TYtJoi
} else {
inputKeyTypeLeft = BuildJoinKeyType(labels.Inputs[0], *leftKeyColumns);
inputKeyTypeRight = BuildJoinKeyType(labels.Inputs[1], *rightKeyColumns);
- outputKeyType = UnifyJoinKeyType(pos, inputKeyTypeLeft, inputKeyTypeRight, ctx);
+ outputKeyType = UnifyJoinKeyType(pos, inputKeyTypeLeft, inputKeyTypeRight, ctx, *state->Types);
}
TVector<TString> ytReduceByColumns;
diff --git a/yt/yql/providers/yt/provider/yql_yt_logical_optimize.cpp b/yt/yql/providers/yt/provider/yql_yt_logical_optimize.cpp
index 3201b993e0f..9f0203ac316 100644
--- a/yt/yql/providers/yt/provider/yql_yt_logical_optimize.cpp
+++ b/yt/yql/providers/yt/provider/yql_yt_logical_optimize.cpp
@@ -2437,7 +2437,7 @@ protected:
}
// derive common type for all join keys in key set
- const TTypeAnnotationNode* commonType = UnifyJoinKeyType(equiJoin.Pos(), srcKeyTypes, ctx);
+ const TTypeAnnotationNode* commonType = UnifyJoinKeyType(equiJoin.Pos(), srcKeyTypes, ctx, *State_->Types);
YQL_ENSURE(commonType);
const TTypeAnnotationNode* commonTypeNoOpt = RemoveOptionalType(commonType);
diff --git a/yt/yt/client/api/options.h b/yt/yt/client/api/options.h
index 08c26b2eeb8..18c0592944a 100644
--- a/yt/yt/client/api/options.h
+++ b/yt/yt/client/api/options.h
@@ -52,6 +52,10 @@ struct TClientOptions
* Consult your cluster administrators for details.
*/
std::optional<std::string> MultiproxyTargetCluster;
+
+ //! If set, a master transaction whose commit fails is abandoned (dropped locally, no
+ //! abort) instead of aborted, so a retrier can re-issue the commit. RPC proxy only.
+ bool AbandonMasterTransactionsOnFailedCommit = false;
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/yt/client/api/rpc_proxy/transaction_impl.cpp b/yt/yt/client/api/rpc_proxy/transaction_impl.cpp
index 4f035ba7da5..096308164bb 100644
--- a/yt/yt/client/api/rpc_proxy/transaction_impl.cpp
+++ b/yt/yt/client/api/rpc_proxy/transaction_impl.cpp
@@ -196,6 +196,20 @@ void TTransaction::Detach()
YT_UNUSED_FUTURE(req->Invoke());
}
+void TTransaction::Abandon(TGuard<NThreading::TSpinLock>* /*guard*/)
+{
+ YT_ASSERT_SPINLOCK_AFFINITY(SpinLock_);
+
+ if (State_ == ETransactionState::Abandoned) {
+ return;
+ }
+
+ // Like Detach, but sends no request: the server tx is left to expire on its own.
+ State_ = ETransactionState::Abandoned;
+
+ YT_LOG_DEBUG("Transaction abandoned");
+}
+
void TTransaction::SubscribeCommitted(const TCommittedHandler& handler)
{
Committed_.Subscribe(handler);
@@ -346,7 +360,15 @@ TFuture<TTransactionCommitResult> TTransaction::Commit(const TTransactionCommitO
if (rspOrError.IsOK() && State_ == ETransactionState::Committing) {
State_ = ETransactionState::Committed;
} else if (!rspOrError.IsOK()) {
- YT_UNUSED_FUTURE(DoAbort(&guard));
+ if (Type_ == ETransactionType::Master &&
+ Client_->GetOptions().AbandonMasterTransactionsOnFailedCommit)
+ {
+ // Keep the (possibly transient/ambiguous) failed commit's
+ // transaction alive for a retrier instead of aborting it.
+ Abandon(&guard);
+ } else {
+ YT_UNUSED_FUTURE(DoAbort(&guard));
+ }
THROW_ERROR_EXCEPTION("Error committing transaction %v",
GetId())
<< rspOrError;
@@ -1190,7 +1212,8 @@ TFuture<void> TTransaction::SendPing()
State_ != ETransactionState::Flushed &&
State_ != ETransactionState::FlushedModifications &&
State_ != ETransactionState::Aborted &&
- State_ != ETransactionState::Detached)
+ State_ != ETransactionState::Detached &&
+ State_ != ETransactionState::Abandoned)
{
State_ = ETransactionState::Aborted;
fireAborted = true;
diff --git a/yt/yt/client/api/rpc_proxy/transaction_impl.h b/yt/yt/client/api/rpc_proxy/transaction_impl.h
index ea39092b39e..69c885fdebe 100644
--- a/yt/yt/client/api/rpc_proxy/transaction_impl.h
+++ b/yt/yt/client/api/rpc_proxy/transaction_impl.h
@@ -24,6 +24,7 @@ DEFINE_ENUM(ETransactionState,
(Aborted)
(AbortFailed)
(Detached)
+ (Abandoned)
);
class TTransaction
@@ -327,6 +328,8 @@ private:
TGuard<NThreading::TSpinLock>* guard,
const TTransactionAbortOptions& options = {});
+ void Abandon(TGuard<NThreading::TSpinLock>* guard);
+
void ValidateActive();
void DoValidateActive();
diff --git a/yt/yt/client/driver/config.cpp b/yt/yt/client/driver/config.cpp
index 390f70f8010..4ffadfa9d67 100644
--- a/yt/yt/client/driver/config.cpp
+++ b/yt/yt/client/driver/config.cpp
@@ -67,6 +67,9 @@ void TDriverConfig::Register(TRegistrar registrar)
registrar.Parameter("require_password_in_authentication_commands", &TThis::RequirePasswordInAuthenticationCommands)
.Default(true);
+ registrar.Parameter("abandon_master_transactions_on_failed_commit", &TThis::AbandonMasterTransactionsOnFailedCommit)
+ .Default(false);
+
registrar.Preprocessor([] (TThis* config) {
config->ClientCache->Capacity = 1024_KB;
config->ProxyDiscoveryCache->RefreshTime = TDuration::Seconds(15);
diff --git a/yt/yt/client/driver/config.h b/yt/yt/client/driver/config.h
index b9bf7ae3ada..7dc01d89ef2 100644
--- a/yt/yt/client/driver/config.h
+++ b/yt/yt/client/driver/config.h
@@ -54,6 +54,10 @@ struct TDriverConfig
//! Controls whether authentication commands (SetUserPassword, IssueToken, ListUserTokens, etc.) require a correct password to be used.
bool RequirePasswordInAuthenticationCommands;
+ //! If set, a master transaction whose commit fails is abandoned (dropped locally, no
+ //! abort) instead of aborted, so a retrier can re-issue the commit. RPC proxy only.
+ bool AbandonMasterTransactionsOnFailedCommit;
+
REGISTER_YSON_STRUCT(TDriverConfig);
static void Register(TRegistrar registrar);
diff --git a/yt/yt/client/driver/driver.cpp b/yt/yt/client/driver/driver.cpp
index 51d04ce67b8..996eecdf469 100644
--- a/yt/yt/client/driver/driver.cpp
+++ b/yt/yt/client/driver/driver.cpp
@@ -66,6 +66,7 @@ static TClientOptions GetRootClientOptions(const TDriverConfigPtr& config)
{
auto result = TClientOptions::Root();
result.MultiproxyTargetCluster = config->MultiproxyTargetCluster;
+ result.AbandonMasterTransactionsOnFailedCommit = config->AbandonMasterTransactionsOnFailedCommit;
return result;
}
@@ -482,6 +483,7 @@ public:
? std::make_optional(New<NAuth::TServiceTicketFixedAuth>(*request.ServiceTicket))
: std::nullopt;
options.MultiproxyTargetCluster = Config_->MultiproxyTargetCluster;
+ options.AbandonMasterTransactionsOnFailedCommit = Config_->AbandonMasterTransactionsOnFailedCommit;
auto client = ClientCache_->Get(identity, options);
diff --git a/yt/yt/core/yson/protobuf_interop.cpp b/yt/yt/core/yson/protobuf_interop.cpp
index 01b909e9652..b9fdd410b66 100644
--- a/yt/yt/core/yson/protobuf_interop.cpp
+++ b/yt/yt/core/yson/protobuf_interop.cpp
@@ -578,6 +578,11 @@ public:
return Underlying_->is_packed() && !IsYsonMap();
}
+ bool IsPackable() const
+ {
+ return Underlying_->is_packable();
+ }
+
bool IsRequired() const
{
return Underlying_->is_required() || Required_;
@@ -2665,7 +2670,15 @@ private:
template <class T>
void ParseFixedPacked(ui64 length, const TProtobufField* field, auto&& func)
{
- YT_ASSERT(length % sizeof(T) == 0);
+ if (length % sizeof(T) != 0) {
+ THROW_ERROR_EXCEPTION(EErrorCode::InvalidProtobufWireFormat,
+ "Packed field %v has length %v which is not a multiple of the element size %v",
+ YPathStack_.GetHumanReadablePath(),
+ length,
+ sizeof(T))
+ << TErrorAttribute("ypath", YPathStack_.GetPath())
+ << TErrorAttribute("proto_field", field->GetFullName());
+ }
for (auto index = 0u; index < length / sizeof(T); ++index) {
T unsignedValue;
auto readResult = false;
@@ -2695,7 +2708,15 @@ private:
const void* data = nullptr;
int size = 0;
CodedStream_.GetDirectBufferPointer(&data, &size);
- YT_ASSERT(length <= static_cast<ui64>(size));
+ if (length > static_cast<ui64>(size)) {
+ THROW_ERROR_EXCEPTION(EErrorCode::InvalidProtobufWireFormat,
+ "Packed field %v has length %v exceeding the remaining buffer size %v",
+ YPathStack_.GetHumanReadablePath(),
+ length,
+ size)
+ << TErrorAttribute("ypath", YPathStack_.GetPath())
+ << TErrorAttribute("proto_field", field->GetFullName());
+ }
ArrayInputStream array(data, length);
CodedInputStream in(&array);
size_t index = 0;
@@ -2729,6 +2750,21 @@ private:
int tag,
WireFormatLite::WireType wireType)
{
+ // Reject a wire type incompatible with the field's declared type;
+ // left unchecked it could be misparsed and abort.
+ auto canonicalWireType = WireFormat::WireTypeForFieldType(field->GetType());
+ bool correctlyPacked = field->IsPackable() &&
+ wireType == WireFormatLite::WIRETYPE_LENGTH_DELIMITED;
+ if (wireType != canonicalWireType && !correctlyPacked) {
+ THROW_ERROR_EXCEPTION(EErrorCode::InvalidProtobufWireFormat,
+ "Invalid wire type %v for field %v; expected %v",
+ static_cast<int>(wireType),
+ YPathStack_.GetHumanReadablePath(),
+ static_cast<int>(canonicalWireType))
+ << TErrorAttribute("ypath", YPathStack_.GetPath())
+ << TErrorAttribute("proto_field", field->GetFullName());
+ }
+
auto storeEnumAsInt = [this, field] (auto value) {
const auto* enumType = field->GetEnumType();
if (field->IsEnumValueCheckStrict() && !enumType->FindLiteralByValue(value)) {
diff --git a/yt/yt/core/yson/public.h b/yt/yt/core/yson/public.h
index 70bd806d5a7..88d0dd0634c 100644
--- a/yt/yt/core/yson/public.h
+++ b/yt/yt/core/yson/public.h
@@ -54,9 +54,10 @@ DEFINE_ENUM(EUnknownYsonFieldsMode,
////////////////////////////////////////////////////////////////////////////////
YT_DEFINE_ERROR_ENUM(
- ((ParseError) (4000))
- ((MemoryLimitExceeded) (4001))
- ((DepthLimitExceeded) (4002))
+ ((ParseError) (4000))
+ ((MemoryLimitExceeded) (4001))
+ ((DepthLimitExceeded) (4002))
+ ((InvalidProtobufWireFormat) (4003))
);
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/yt/core/yson/unittests/protobuf_yson_ut.cpp b/yt/yt/core/yson/unittests/protobuf_yson_ut.cpp
index 31eec2acbe7..3ba4da18b3c 100644
--- a/yt/yt/core/yson/unittests/protobuf_yson_ut.cpp
+++ b/yt/yt/core/yson/unittests/protobuf_yson_ut.cpp
@@ -1012,6 +1012,23 @@ TEST(TYsonToProtobufTest, KeepUnknownFields)
EXPECT_TRUE(AreNodesEqual(ConvertToNode(TYsonString(newYsonString)), ConvertToNode(ysonString)));
}
+TEST(TYsonToProtobufTest, RejectWireTypeMismatch)
+{
+ // Field 2 is length-delimited in TNestedMessage and varint in TMessage.
+ NYT::NYson::NProto::TNestedMessage message;
+ message.mutable_nested_message()->set_int32_field(42);
+ TString protobufString = message.SerializeAsString();
+
+ TString ysonString;
+ TStringOutput ysonOutputStream(ysonString);
+ TYsonWriter ysonWriter(&ysonOutputStream, EYsonFormat::Pretty);
+ ArrayInputStream protobufInput(protobufString.data(), protobufString.length());
+
+ EXPECT_THROW_WITH_ERROR_CODE(
+ ParseProtobuf(&ysonWriter, &protobufInput, ReflectProtobufMessageType<NYT::NYson::NProto::TMessage>()),
+ EErrorCode::InvalidProtobufWireFormat);
+}
+
TEST(TYsonToProtobufTest, Entities)
{
TProtobufWriterOptions options;
diff --git a/yt/yt/core/ytree/attribute_filter.cpp b/yt/yt/core/ytree/attribute_filter.cpp
index a63d70c8354..8bbe238f490 100644
--- a/yt/yt/core/ytree/attribute_filter.cpp
+++ b/yt/yt/core/ytree/attribute_filter.cpp
@@ -11,6 +11,7 @@
#include <yt/yt/core/yson/async_writer.h>
#include <yt/yt/core/yson/async_consumer.h>
+#include <yt/yt/core/yson/attribute_consumer.h>
#include <yt/yt/core/yson/pull_parser.h>
#include <yt/yt/core/yson/string_filter.h>
@@ -360,6 +361,49 @@ void TAttributeFilter::Remove(const std::vector<IAttributeDictionary::TKey>& key
);
}
+void WriteAttributeDictionaryFragment(
+ IAsyncYsonConsumer* consumer,
+ const IAttributeDictionary& attributes,
+ const TAttributeFilter& attributeFilter,
+ bool stable)
+{
+ auto pairs = attributes.ListPairs();
+ if (stable) {
+ std::sort(pairs.begin(), pairs.end(), [] (const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ }
+
+ TAttributeFilter::TKeyToFilter keyToFilter;
+ if (attributeFilter) {
+ keyToFilter = attributeFilter.Normalize();
+ }
+
+ for (const auto& [key, value] : pairs) {
+ if (!attributeFilter) {
+ consumer->OnKeyedItem(key);
+ consumer->OnRaw(value);
+ } else if (auto it = keyToFilter.find(key); it != keyToFilter.end()) {
+ const auto& pathFilter = it->second;
+ TAttributeValueConsumer valueConsumer(consumer, key);
+ auto filteringConsumer = TAttributeFilter::CreateFilteringConsumer(&valueConsumer, pathFilter);
+ filteringConsumer->GetConsumer()->OnRaw(value);
+ filteringConsumer->Finish();
+ }
+ }
+}
+
+void WriteAttributeDictionary(
+ IAsyncYsonConsumer* consumer,
+ const IAttributeDictionary& attributes,
+ const TAttributeFilter& attributeFilter,
+ bool stable)
+{
+ TAttributeFragmentConsumer attributesConsumer(consumer);
+ WriteAttributeDictionaryFragment(&attributesConsumer, attributes, attributeFilter, stable);
+ attributesConsumer.Finish();
+}
+
std::unique_ptr<TAttributeFilter::IFilteringConsumer> TAttributeFilter::CreateFilteringConsumer(
IYsonConsumer* targetConsumer,
const TPathFilter& pathFilter)
diff --git a/yt/yt/core/ytree/attribute_filter.h b/yt/yt/core/ytree/attribute_filter.h
index 3cbb28dc552..9fff47f3d68 100644
--- a/yt/yt/core/ytree/attribute_filter.h
+++ b/yt/yt/core/ytree/attribute_filter.h
@@ -169,6 +169,18 @@ void FormatValue(
const TAttributeFilter& attributeFilter,
TStringBuf /*spec*/);
+void WriteAttributeDictionaryFragment(
+ NYson::IAsyncYsonConsumer* consumer,
+ const IAttributeDictionary& attributes,
+ const TAttributeFilter& attributeFilter,
+ bool stable);
+
+void WriteAttributeDictionary(
+ NYson::IAsyncYsonConsumer* consumer,
+ const IAttributeDictionary& attributes,
+ const TAttributeFilter& attributeFilter,
+ bool stable);
+
////////////////////////////////////////////////////////////////////////////////
struct TShrunkAttributeFilterView
diff --git a/yt/yt/core/ytree/ephemeral_node_factory.cpp b/yt/yt/core/ytree/ephemeral_node_factory.cpp
index 8b4dcbc31b9..27bd955df50 100644
--- a/yt/yt/core/ytree/ephemeral_node_factory.cpp
+++ b/yt/yt/core/ytree/ephemeral_node_factory.cpp
@@ -58,33 +58,7 @@ public:
return;
}
- const auto& attributes = Attributes();
-
- auto pairs = attributes.ListPairs();
- if (stable) {
- std::sort(pairs.begin(), pairs.end(), [] (const auto& lhs, const auto& rhs) {
- return lhs.first < rhs.first;
- });
- }
-
- TAttributeFilter::TKeyToFilter keyToFilter;
- if (attributeFilter) {
- keyToFilter = attributeFilter.Normalize();
- }
-
- for (const auto& [key, value] : pairs) {
- if (!attributeFilter) {
- // A fast path for taking the whole attribute.
- consumer->OnKeyedItem(key);
- consumer->OnRaw(value);
- } else if (auto it = keyToFilter.find(key); it != keyToFilter.end()) {
- const auto& pathFilter = it->second;
- TAttributeValueConsumer valueConsumer(consumer, key);
- auto filteringConsumer = TAttributeFilter::CreateFilteringConsumer(&valueConsumer, pathFilter);
- filteringConsumer->GetConsumer()->OnRaw(value);
- filteringConsumer->Finish();
- }
- }
+ WriteAttributeDictionaryFragment(consumer, Attributes(), attributeFilter, stable);
}
protected:
diff --git a/yt/yt/core/ytree/unittests/attribute_filter_ut.cpp b/yt/yt/core/ytree/unittests/attribute_filter_ut.cpp
index b2f8573e97a..4f016599d59 100644
--- a/yt/yt/core/ytree/unittests/attribute_filter_ut.cpp
+++ b/yt/yt/core/ytree/unittests/attribute_filter_ut.cpp
@@ -1,10 +1,38 @@
#include <yt/yt/core/test_framework/framework.h>
#include <yt/yt/core/ytree/attribute_filter.h>
+#include <yt/yt/core/ytree/convert.h>
+#include <yt/yt/core/ytree/helpers.h>
+
+#include <yt/yt/core/concurrency/scheduler_api.h>
+
+#include <yt/yt/core/yson/async_writer.h>
namespace NYT::NYTree {
using namespace NYPath;
+using namespace NYson;
+using namespace NConcurrency;
+
+////////////////////////////////////////////////////////////////////////////////
+
+TYsonString WriteAttributesToYson(
+ const IAttributeDictionary& attributes,
+ const TAttributeFilter& attributeFilter)
+{
+ TAsyncYsonWriter writer;
+ writer.OnBeginMap();
+ WriteAttributeDictionaryFragment(&writer, attributes, attributeFilter, /*stable*/ true);
+ writer.OnEndMap();
+ return WaitForFast(writer.Finish())
+ .ValueOrThrow();
+}
+
+std::string NormalizeYson(TYsonString yson)
+{
+ return ConvertToYsonString(ConvertToNode(yson), EYsonFormat::Text)
+ .ToString();
+}
////////////////////////////////////////////////////////////////////////////////
@@ -87,6 +115,28 @@ TEST(TAttributeFilterTest, TestNormalization)
TAttributeFilter({}, {"/yp/foo", "/y\\x70/foo/qux", "/yt/bar"}).Normalize());
}
+TEST(TAttributeFilterTest, WriteAttributeDictionaryFragment)
+{
+ auto attributes = CreateEphemeralAttributes();
+ attributes->Set("z", 1);
+ attributes->Set("a", "value");
+ attributes->SetYson("nested", TYsonString(TStringBuf("{foo={bar=42; baz=24}; qux=3}")));
+
+ auto wholeResult = WriteAttributesToYson(
+ *attributes,
+ TAttributeFilter({"z", "a"}));
+ EXPECT_EQ(
+ NormalizeYson(TYsonString(TStringBuf("{a=value; z=1}"))),
+ NormalizeYson(wholeResult));
+
+ auto filteredResult = WriteAttributesToYson(
+ *attributes,
+ TAttributeFilter({}, {"/nested/foo/bar"}));
+ EXPECT_EQ(
+ NormalizeYson(TYsonString(TStringBuf("{nested={foo={bar=42}}}"))),
+ NormalizeYson(filteredResult));
+}
+
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT::NYTree