Files
shadowsocks-libev/tests/alloc_audit/check_steady.py
T
Max Lv 8d39cd7569 C++20 port 6/14: allocation-free AEAD codecs (#3070)
* Add allocation-free C++ AEAD codecs

src/crypto/aead.hpp implements the SIP004/SIP007 AEAD ciphers as value
types: AeadEngine holds the libsodium or Mbed TLS GCM state by value,
AeadTcpEncryptor/AeadTcpDecryptor frame the chunk stream with a fixed
reassembly buffer, and aead_udp_seal/open handle packets. HKDF-SHA1 runs
on stack SHA-1 state instead of Mbed TLS's allocating HMAC API.

The bundled Mbed TLS config now enables MBEDTLS_BLOCK_CIPHER_C, which
keeps the AES context inside mbedtls_gcm_context, so GCM key setup no
longer allocates. That alone saves the C programs two allocations per
connection.

test_crypto_aead_cpp checks HKDF against RFC 5869 and crypto_hkdf, runs
TCP streams and UDP packets between the C and C++ implementations in
both directions for all five AEAD methods, and covers tampering,
framing and salt replay. alloc_steady_crypto_aead runs it under the
malloc counter and requires zero allocations across 600 new
connections and 600 packets (tests/alloc_audit/check_steady.py).

ss_strict_warnings() now applies -Wextra per source file so targets
mixing new C++ with converted C modules keep the latter at -Wall, and
Result gains move assignment.

* Include <algorithm> for std::min in test_crypto_aead_cpp.cpp

* Build the C++ AEAD codecs against Mbed TLS 2.x

Mbed TLS 2 returns void from mbedtls_sha1_starts/update/finish (the int
versions are the _ret variants), which the system-library build on
Ubuntu (2.28) rejects. crypto/mbedtls_compat.hpp gives one int-returning
spelling for both.

* Include C++ headers before crypto.h in test_crypto_aead_cpp.cpp

crypto.h defines min/max macros for the C sources; libstdc++ headers
included after it fail to parse.

* Disable the zero-allocation checks with a system Mbed TLS

They hold for the bundled Mbed TLS (MBEDTLS_BLOCK_CIPHER_C); a system
one may allocate in GCM key setup, as the Debian package build shows.
2026-10-15 09:56:12 +08:00

51 lines
2.0 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-or-later
"""Run a test program under the alloc_counter shim and check a steady phase.
The program calls ss::test::alloc_mark() (tests/alloc_audit/alloc_probe.hpp)
before and after the code that must not allocate. This script preloads the
shim, runs the program, and fails unless exactly two marks were recorded and
no allocation happened between them.
Usage: check_steady.py --shim LIB [--max N] -- PROGRAM [ARGS...]
"""
import argparse
import os
from pathlib import Path
import subprocess
import sys
import tempfile
def main():
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--shim", required=True)
parser.add_argument("--max", type=int, default=0, help="allocations allowed between the marks")
parser.add_argument("command", nargs=argparse.REMAINDER)
args = parser.parse_args()
command = args.command[1:] if args.command[:1] == ["--"] else args.command
if not command:
parser.error("missing program to run")
with tempfile.TemporaryDirectory(prefix="ss-steady-") as temp:
out = Path(temp) / "counts"
env = os.environ.copy()
env["DYLD_INSERT_LIBRARIES" if sys.platform == "darwin" else "LD_PRELOAD"] = str(Path(args.shim).resolve())
env["SS_ALLOC_AUDIT_OUT"] = str(out)
result = subprocess.run(command, env=env)
if result.returncode != 0:
print(f"{command[0]} exited with {result.returncode}")
return 1
lines = out.read_text().splitlines() if out.exists() else []
if len(lines) != 2:
print(f"expected 2 allocation marks, got {len(lines)}")
return 1
before, after = ([int(v) for v in line.split()] for line in lines)
allocations = after[1] - before[1]
print(f"steady phase: {allocations} allocation(s), {after[3] - before[3]} bytes (allowed: {args.max})")
return 0 if allocations <= args.max else 1
if __name__ == "__main__":
sys.exit(main())