diff options
| author | YDBot <[email protected]> | 2026-07-16 01:03:32 +0000 |
|---|---|---|
| committer | YDBot <[email protected]> | 2026-07-16 01:03:32 +0000 |
| commit | 4fd409e7518f252e69af6a97c9985ac75217bc05 (patch) | |
| tree | dfeb8933f25774369a38a16c0347369bca7e2a04 | |
| parent | 73c8481330d15583a0f3f94aa7e496077a700d53 (diff) | |
| parent | 391306e72edb239eb7ac945e2d2c72a40564ff02 (diff) | |
Sync branches 260716-0101
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(®, 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, ®, 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, ®, 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, ®); - 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(®); - 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(®); - 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(®->area), + .region_ptr = uring_ptr_to_u64(®->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, ®); - 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, ®); - if (!ret) { - fprintf(stderr, "registered taken queue\n"); - return T_EXIT_FAIL; + default_reg(®, 0); + ret = try_register_zcrx(®.zcrx); + if (ret == -EPERM) + return ret; + if (ret) { + fprintf(stderr, "default setup failed\n"); + return ret; } - ret = io_uring_register_ifq(&ring2, ®); - if (!ret) { - fprintf(stderr, "registered taken queue ring2\n"); - return T_EXIT_FAIL; + if (def_hugepage_area_mem) { + default_reg(®, CONFIG_HUGEPAGE); + ret = try_register_zcrx(®.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(®, 0); + reg.zcrx.region_ptr = 0; + ret = try_register_zcrx(®.zcrx); + if (ret != -EINVAL && ret != -EFAULT) { + fprintf(stderr, "registered w/o region\n"); + return ret; + } - ret = try_register_ifq(®); - if (ret != -EINVAL) { - fprintf(stderr, "registered 0 rq entries\n"); - return T_EXIT_FAIL; + default_reg(®, 0); + reg.rq_region.user_addr = 0; + ret = try_register_zcrx(®.zcrx); + if (ret != -EINVAL && ret != -EFAULT) { + fprintf(stderr, "registered rq region with no memory\n"); + return ret; } - reg.rq_entries = (__u32)-1; + default_reg(®, 0); + reg.rq_region.size = 0; + ret = try_register_zcrx(®.zcrx); + if (ret != -EINVAL && ret != -EFAULT) { + fprintf(stderr, "registered rq region size=0\n"); + return ret; + } - ret = try_register_ifq(®); - if (ret != -EINVAL) { - fprintf(stderr, "registered unlimited nr of rq entries\n"); - return T_EXIT_FAIL; + default_reg(®, 0); + entries = reg.zcrx.rq_entries; + reg.zcrx.rq_entries -= 1; + ret = try_register_zcrx(®.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(®); - if (ret != 0) { - fprintf(stderr, "ifq registration failed %i\n", ret); - return T_EXIT_FAIL; + default_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(®.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(®, 0); + reg.zcrx.rq_entries = 0; + reg.zcrx.rq_entries = ~reg.zcrx.rq_entries; + ret = try_register_zcrx(®.zcrx); + if (ret != -EINVAL && ret != -EFAULT) { + fprintf(stderr, "registered rq with unlimited entries\n"); + return ret; } - return T_EXIT_PASS; + default_reg(®, 0); + reg.zcrx.rq_entries += (page_size / sizeof(struct io_uring_zcrx_rqe)); + ret = try_register_zcrx(®.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(®, 0); + reg.area.len = 0; + ret = try_register_zcrx(®.zcrx); + if (ret != -EINVAL && ret != -EFAULT) { + fprintf(stderr, "registered area size=0, %i\n", ret); + return ret; + } - ret = try_register_ifq(®); - return ret ? T_EXIT_PASS : T_EXIT_FAIL; -} + default_reg(®, 0); + reg.area.addr = 0; + ret = try_register_zcrx(®.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(®, 0); + reg.zcrx.area_ptr = 0; + ret = try_register_zcrx(®.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(®, 0); + reg.area.addr -= page_size; + ret = try_register_zcrx(®.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(®, 0); + reg.area.len += page_size; + ret = try_register_zcrx(®.zcrx); + if (ret != -EFAULT) { + fprintf(stderr, "registered higher than area\n"); + return ret; + } + + default_reg(®, 0); + reg.area.len -= page_size / 2; + ret = try_register_zcrx(®.zcrx); + if (ret != -EFAULT && ret != -EINVAL) { + fprintf(stderr, "registered unaligned area size\n"); + return ret; + } - ret = try_register_ifq(®); - return ret ? T_EXIT_PASS : T_EXIT_FAIL; + default_reg(®, 0); + reg.area.len /= 2; + reg.area.addr += page_size / 2; + ret = try_register_zcrx(®.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(®)) - return T_EXIT_FAIL; - - area_reg.addr = uring_ptr_to_u64(area); - area_reg.len = AREA_SZ - 1; - ret = try_register_ifq(®); - return ret ? T_EXIT_PASS : T_EXIT_FAIL; + ret = try_register_zcrx(®->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(®, 0); + reg.zcrx.rx_buf_len = ~reg.zcrx.rx_buf_len; + ret = try_register_zcrx(®.zcrx); + if (ret != -EINVAL && ret != -ERANGE && ret != -EOVERFLOW) { + fprintf(stderr, "registered UINT_MAX rx buf len\n"); + return ret; + } + + default_reg(®, 0); + reg.zcrx.rx_buf_len = 1U << 31; + ret = try_register_zcrx(®.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(®); - 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(®); - 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, ®); - 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(®, 0); + ret = io_uring_register_ifq(&r1, ®.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(®, 0); + ret = io_uring_register_ifq(&ring_exp, ®.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(®, 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, ®.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, ®); - 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(®, 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(®.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); @@ -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 |
