mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-31 06:23:52 +00:00
Fix CI on Ubuntu 26.04 runners; use string_view over raw string pointers
- Define _GLIBCXX_NO_ASSERTIONS: libstdc++ 15 enables assertions at -O0, which pulls __glibcxx_assert_fail (C++ runtime) into the programs. - test_redir_qemu: boot the guest with -cpu max so the runner's glibc loader runs under TCG. - find_program: std::string_view PATH splitting into a fixed buffer, no heap and no null snprintf destination (GCC -Wformat-truncation). - manager: parse /proc/net/udp numbers with strtoul (cert-err34-c); walk /proc/<pid>/cmdline with std::string_view. - ssurl: sanitise the tag through std::span. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,10 @@ function(ss_project_options target)
|
||||
# it when they are rewritten.
|
||||
target_compile_options(${target} PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions -fno-rtti -fno-threadsafe-statics>)
|
||||
# libstdc++ 15 enables its assertions at -O0, and they call
|
||||
# __glibcxx_assert_fail from the C++ runtime, which the programs and
|
||||
# libshadowsocks-c must not need (scripts/check_cxx_runtime.py).
|
||||
target_compile_definitions(${target} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:_GLIBCXX_NO_ASSERTIONS>)
|
||||
# GCC's C++ -Wall adds -Wsign-compare and -Wclass-memaccess, which C's
|
||||
# -Wall does not have. Modules converted from C keep the warning set
|
||||
# they were written against; ss_strict_warnings() turns both back on.
|
||||
|
||||
+14
-9
@@ -44,6 +44,7 @@
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <new>
|
||||
#include <string_view>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -115,13 +116,15 @@ udp_sender_uid(const struct sockaddr_storage *from, uid_t *uid)
|
||||
if (fgets(line, sizeof(line), f) != NULL) { // header
|
||||
while (!found && fgets(line, sizeof(line), f) != NULL) {
|
||||
char local[64];
|
||||
unsigned port;
|
||||
unsigned long owner;
|
||||
char port_hex[9];
|
||||
char owner_dec[21];
|
||||
// sl local_address rem_address st tx:rx tr:when retrnsmt uid
|
||||
if (sscanf(line, " %*s %63[0-9A-Fa-f]:%x %*s %*s %*s %*s %*s %lu", local, &port, &owner) != 3
|
||||
|| port != want_port) {
|
||||
if (sscanf(line, " %*s %63[0-9A-Fa-f]:%8[0-9A-Fa-f] %*s %*s %*s %*s %*s %20[0-9]",
|
||||
local, port_hex, owner_dec) != 3
|
||||
|| strtoul(port_hex, NULL, 16) != want_port) {
|
||||
continue;
|
||||
}
|
||||
unsigned long owner = strtoul(owner_dec, NULL, 10);
|
||||
// The kernel prints each 32-bit word of the address as a host
|
||||
// order integer; zeros mean the socket is unbound (wildcard).
|
||||
uint8_t bytes[16];
|
||||
@@ -1041,13 +1044,15 @@ is_managed_server(int pid)
|
||||
char cmdline[4096];
|
||||
size_t n = fread(cmdline, 1, sizeof(cmdline), f);
|
||||
fclose(f);
|
||||
for (size_t i = 0; i < n;) {
|
||||
const char *arg = cmdline + i;
|
||||
size_t len = strnlen(arg, n - i);
|
||||
if (len == strlen("--manager-address") && memcmp(arg, "--manager-address", len) == 0) {
|
||||
// NUL-separated arguments; the last one may be cut short.
|
||||
std::string_view rest(cmdline, n);
|
||||
while (!rest.empty()) {
|
||||
std::string_view::size_type nul = rest.find('\0');
|
||||
std::string_view arg(rest.data(), nul != std::string_view::npos ? nul : rest.size());
|
||||
if (arg == "--manager-address") {
|
||||
return true;
|
||||
}
|
||||
i += len + 1;
|
||||
rest.remove_prefix(nul != std::string_view::npos ? nul + 1 : rest.size());
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
|
||||
+46
-26
@@ -4,12 +4,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "utils.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
@@ -118,32 +122,51 @@ ss_process_user(struct ss_process *process, const char *user)
|
||||
|
||||
/* The executable execve() runs: `program` itself if it names a path, or the
|
||||
* first match on PATH, as posix_spawnp() would find it. */
|
||||
static char *
|
||||
find_program(const char *program)
|
||||
using ProgramPath = std::array<char, PATH_MAX>;
|
||||
|
||||
static bool
|
||||
set_path(ProgramPath &out, std::string_view dir, std::string_view program)
|
||||
{
|
||||
if (strchr(program, '/') != NULL) {
|
||||
return copy_string(program);
|
||||
std::string_view slash = dir.empty() ? "" : "/";
|
||||
if (dir.size() + slash.size() + program.size() >= out.size()) {
|
||||
return false;
|
||||
}
|
||||
const char *path = getenv("PATH");
|
||||
if (path == NULL || *path == '\0') {
|
||||
auto end = std::copy(dir.begin(), dir.end(), out.begin());
|
||||
end = std::copy(slash.begin(), slash.end(), end);
|
||||
end = std::copy(program.begin(), program.end(), end);
|
||||
*end = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string_view
|
||||
env_value(const char *name)
|
||||
{
|
||||
const char *value = getenv(name);
|
||||
return value != NULL ? std::string_view(value) : std::string_view();
|
||||
}
|
||||
|
||||
static bool
|
||||
find_program(std::string_view program, ProgramPath &out)
|
||||
{
|
||||
if (program.find('/') != std::string_view::npos) {
|
||||
return set_path(out, {}, program);
|
||||
}
|
||||
std::string_view path = env_value("PATH");
|
||||
if (path.empty()) {
|
||||
path = "/usr/local/bin:/usr/bin:/bin";
|
||||
}
|
||||
size_t room = strlen(path) + strlen(program) + 2;
|
||||
char *candidate = (char *)ss_malloc(room); // heap-ok: plugin start, startup
|
||||
for (const char *dir = path;; ) {
|
||||
const char *colon = strchr(dir, ':');
|
||||
size_t len = colon != NULL ? (size_t)(colon - dir) : strlen(dir);
|
||||
snprintf(candidate, room, "%.*s%s%s", (int)len, dir, len > 0 ? "/" : "", program);
|
||||
if (access(candidate, X_OK) == 0) {
|
||||
return candidate;
|
||||
for (;;) {
|
||||
// Not substr(): it can throw, which would pull in the C++ runtime.
|
||||
std::string_view::size_type colon = path.find(':');
|
||||
std::string_view dir(path.data(), colon != std::string_view::npos ? colon : path.size());
|
||||
if (set_path(out, dir, program) && access(out.data(), X_OK) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (colon == NULL) {
|
||||
break;
|
||||
if (colon == std::string_view::npos) {
|
||||
return false;
|
||||
}
|
||||
dir = colon + 1;
|
||||
path.remove_prefix(colon + 1);
|
||||
}
|
||||
free(candidate); // heap-ok: plugin start, startup
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* In the forked child: lose root, but keep the right to bind ports below
|
||||
@@ -180,16 +203,14 @@ drop_in_child(const struct ss_process *process)
|
||||
static int
|
||||
fork_as_user(struct ss_process *process, char **env)
|
||||
{
|
||||
char *path = find_program(process->program);
|
||||
if (path == NULL) {
|
||||
ProgramPath path;
|
||||
if (!find_program(process->program, path)) {
|
||||
return ENOENT;
|
||||
}
|
||||
/* The child reports a failed switch or exec through this pipe. */
|
||||
int report[2];
|
||||
if (pipe(report) != 0) {
|
||||
int error = errno;
|
||||
free(path); // heap-ok: plugin start, startup
|
||||
return error;
|
||||
return errno;
|
||||
}
|
||||
(void)fcntl(report[1], F_SETFD, FD_CLOEXEC);
|
||||
pid_t pid = fork();
|
||||
@@ -204,7 +225,7 @@ fork_as_user(struct ss_process *process, char **env)
|
||||
signal(SIGINT, SIG_DFL);
|
||||
int error = EPERM;
|
||||
if (drop_in_child(process) == 0) {
|
||||
execve(path, process->argv, env); // NOLINT(clang-analyzer-optin.taint.GenericTaint): the PATH lookup posix_spawnp() would do
|
||||
execve(path.data(), process->argv, env); // NOLINT(clang-analyzer-optin.taint.GenericTaint): the PATH lookup posix_spawnp() would do
|
||||
error = errno;
|
||||
}
|
||||
ssize_t ignored = write(report[1], &error, sizeof(error));
|
||||
@@ -227,7 +248,6 @@ fork_as_user(struct ss_process *process, char **env)
|
||||
}
|
||||
}
|
||||
close(report[0]);
|
||||
free(path); // heap-ok: plugin start, startup
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -29,6 +29,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <span>
|
||||
|
||||
#include "base64.h"
|
||||
#include "ssurl.h"
|
||||
@@ -253,9 +254,9 @@ ss_url_parse(const char *uri, ss_url_t *url)
|
||||
url->tag = percent_decode(hash + 1, body_len - (size_t)(hash - body) - 1);
|
||||
/* It is only logged: no control characters (newlines, escapes)
|
||||
* from a link may reach the log. */
|
||||
for (char *c = url->tag; *c != '\0'; c++) {
|
||||
if ((unsigned char)*c < 0x20 || *c == 0x7f) {
|
||||
*c = '?';
|
||||
for (char &c : std::span<char>(url->tag, strlen(url->tag))) {
|
||||
if ((unsigned char)c < 0x20 || c == 0x7f) {
|
||||
c = '?';
|
||||
}
|
||||
}
|
||||
body_len = (size_t)(hash - body);
|
||||
|
||||
@@ -222,6 +222,7 @@ SERIAL_LOG="$WORK_DIR/serial.log"
|
||||
|
||||
qemu-system-x86_64 \
|
||||
$KVM_FLAG \
|
||||
-cpu max \
|
||||
-m 512 \
|
||||
-kernel "$WORK_DIR/vmlinuz" \
|
||||
-initrd "$WORK_DIR/initramfs.gz" \
|
||||
|
||||
Reference in New Issue
Block a user