ci: fix test_subagent strcasestr crash; llvm-mingw Windows zip

clang under strict C11 left strcasestr undeclared (returns int → pointer
truncation → SIGSEGV). Parse Content-Length portably; keep -Werror on
make check. Drop Nix for windows-cross (root /nix fails) and use pinned
llvm-mingw UCRT instead; Alpine musl gets linux-headers for audit.h.
This commit is contained in:
WHC
2026-10-07 02:39:36 +08:00
parent 420f9b242c
commit 9b2c7acfae
3 changed files with 87 additions and 26 deletions
+10 -23
View File
@@ -37,7 +37,11 @@ jobs:
- name: Build (warnings are errors)
run: make CC=${{ matrix.cc }} CFLAGS="-std=c11 -Wall -Wextra -O2 -g -Werror" -j"$(nproc)"
- name: Unit tests
run: make CC=${{ matrix.cc }} check
run: |
# Same -Werror as the build step so undeclared libc helpers cannot
# silently return int and truncate pointers (see test_subagent).
make CC=${{ matrix.cc }} \
CFLAGS="-std=c11 -Wall -Wextra -O2 -g -Werror" check
- name: CMake build + ctest
run: |
cmake -B build -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCST_BUILD_TUI=ON
@@ -69,7 +73,10 @@ jobs:
-o /tmp/ci_checkout.sh
bash /tmp/ci_checkout.sh
- name: Install build dependencies
run: apk add --no-cache build-base pkgconf curl-dev ncurses-dev git nodejs bash python3
run: |
# linux-headers provides linux/audit.h for the seccomp probe path
apk add --no-cache build-base pkgconf curl-dev ncurses-dev \
linux-headers git nodejs bash python3
- name: Build + unit tests
run: |
make CFLAGS="-std=c11 -Wall -Wextra -O2 -Werror" -j"$(nproc)"
@@ -97,28 +104,8 @@ jobs:
$SUDO apt-get install -y --no-install-recommends \
ca-certificates curl git cmake ninja-build make pkg-config \
python3 zip unzip perl xz-utils
- name: Install Nix (TUNA)
run: |
if ! command -v nix-shell >/dev/null; then
curl -L https://mirrors.tuna.tsinghua.edu.cn/nix/latest/install | sh -s -- --no-daemon
fi
mkdir -p "$HOME/.config/nix"
printf '%s\n' \
'substituters = https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store https://cache.nixos.org/' \
'trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=' \
> "$HOME/.config/nix/nix.conf"
# shellcheck source=/dev/null
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
nix --version
- name: Proxy GitHub for third_party clones
run: |
# curl/busybox-w32/ncurses sources; skip if the proxy is down
git config --global url."https://gitclone.com/github.com/".insteadOf "https://github.com/"
- name: Cross-build Windows zip
run: |
# shellcheck source=/dev/null
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
nix-shell shell-win.nix --run 'scripts/bootstrap_windows.sh .'
run: bash scripts/ci_windows_cross.sh
- name: Verify zip
run: |
ls -lah dist/windows-x86_64/
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# CI helper: MinGW-w64 UCRT toolchain via llvm-mingw (no Nix / no /nix).
# Downloads a pinned release, puts it on PATH, then runs bootstrap_windows.sh.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
VER="${CST_LLVM_MINGW_VER:-20241030}"
NAME="llvm-mingw-${VER}-ucrt-ubuntu-20.04-x86_64"
TGZ="/tmp/${NAME}.tar.xz"
# Prefer /opt when writable (root CI); else unpack under $HOME.
if [[ -w /opt || "$(id -u)" -eq 0 ]]; then
DIR="/opt/${NAME}"
PARENT=/opt
else
DIR="${HOME}/${NAME}"
PARENT="$HOME"
fi
if [[ ! -x "$DIR/bin/x86_64-w64-mingw32-gcc" ]]; then
echo "==> download $NAME"
urls=(
"https://gitclone.com/github.com/mstorsjo/llvm-mingw/releases/download/${VER}/${NAME}.tar.xz"
"https://mirror.ghproxy.com/https://github.com/mstorsjo/llvm-mingw/releases/download/${VER}/${NAME}.tar.xz"
"https://github.com/mstorsjo/llvm-mingw/releases/download/${VER}/${NAME}.tar.xz"
)
ok=0
for u in "${urls[@]}"; do
echo " try $u"
if curl -fL --retry 2 --connect-timeout 20 --retry-delay 2 -o "$TGZ" "$u"; then
ok=1
break
fi
done
[[ $ok -eq 1 ]] || { echo "failed to download llvm-mingw" >&2; exit 1; }
rm -rf "$DIR"
mkdir -p "$PARENT"
tar -C "$PARENT" -xf "$TGZ"
fi
export PATH="$DIR/bin:$PATH"
export MINGW_HOST=x86_64-w64-mingw32
export BUILD_CC="${BUILD_CC:-cc}"
need() { command -v "$1" >/dev/null || { echo "missing $1" >&2; exit 1; }; }
need x86_64-w64-mingw32-gcc
need x86_64-w64-mingw32-windres
need cmake; need ninja; need perl; need zip; need git; need make
if ! printf '#include <stdio.h>\n' | x86_64-w64-mingw32-gcc -dM -E - | grep -qw _UCRT; then
echo "toolchain is not UCRT" >&2
exit 1
fi
# Third-party sources live on GitHub; prefer a CN-friendly rewrite.
git config --global url."https://gitclone.com/github.com/".insteadOf "https://github.com/" || true
echo "==> bootstrap_windows.sh"
exec bash scripts/bootstrap_windows.sh "$ROOT"
+18 -3
View File
@@ -18,6 +18,7 @@
#define CST_TEST_SOCKETS
#include "cst_test_util.h"
#include <ctype.h>
#include <curl/curl.h>
#include <pthread.h>
#include <stdatomic.h>
@@ -52,6 +53,20 @@ static void send_all(test_sock_t fd, const char *s, size_t n) {
}
}
/* Portable Content-Length lookup (avoid GNU strcasestr; undeclared under
* strict C11 it is assumed to return int and the pointer truncates). */
static const char *find_content_length(const char *hdrs) {
static const char key[] = "content-length:";
for (const char *p = hdrs; *p; p++) {
size_t i = 0;
while (key[i] && tolower((unsigned char)p[i]) == (unsigned char)key[i])
i++;
if (key[i] == '\0')
return p + (sizeof(key) - 1);
}
return NULL;
}
static void sse(test_sock_t fd, cJSON *delta, const char *finish) {
cJSON *o = cJSON_CreateObject();
cJSON *ch = cJSON_AddArrayToObject(o, "choices");
@@ -205,7 +220,7 @@ static void handle(test_sock_t fd, const char *body_txt) {
const char *rep = cJSON_IsString(c) ? strstr(c->valuestring,
"child report ")
: NULL;
if (strcmp(r->valuestring, "tool") == 0 && rep)
if (cJSON_IsString(r) && strcmp(r->valuestring, "tool") == 0 && rep)
cst_buf_printf(&b, " [%.14s]", rep);
}
reply_text(fd, b.buf);
@@ -230,8 +245,8 @@ static void *conn_main(void *p) {
char *e = strstr(req.buf, "\r\n\r\n");
if (e) {
hdr_end = (size_t)(e - req.buf) + 4;
char *cl = strcasestr(req.buf, "Content-Length:");
need = cl ? atol(cl + 15) : 0;
const char *cl = find_content_length(req.buf);
need = cl ? atol(cl) : 0;
}
}
if (hdr_end && (long)(req.len - hdr_end) >= need)