Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@ jobs:
clang-tidy:
runs-on: ubuntu-latest
env:
# Ratchet: number of pre-existing clang-tidy warnings (see .clang-tidy),
# Ratchet: number of known clang-tidy warnings (see .clang-tidy),
# counted with clang-tidy-18 (pinned below so runner upgrades don't move
# the number). CI fails if the count goes UP; lower this value as
# findings get fixed.
MAX_WARNINGS: 29
# the number). All baseline findings have been fixed or triaged
# (false positives carry NOLINT comments with rationale), so any new
# warning fails CI.
MAX_WARNINGS: 0
steps:
- uses: actions/checkout@v6
with:
Expand Down
5 changes: 5 additions & 0 deletions src/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ cache_delete(struct cache *cache, int keep_data)
HASH_CLEAR(hh, cache->entries);
} else {
HASH_ITER(hh, cache->entries, entry, tmp){
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): uthash unlinks here; entry is freed afterwards
HASH_DEL(cache->entries, entry);
if (entry->data != NULL) {
if (cache->free_cb) {
Expand Down Expand Up @@ -129,6 +130,7 @@ cache_clear(struct cache *cache, ev_tstamp age)

HASH_ITER(hh, cache->entries, entry, tmp){
if (now - entry->ts > age) {
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): uthash unlinks here; entry is freed afterwards
HASH_DEL(cache->entries, entry);
if (entry->data != NULL) {
if (cache->free_cb) {
Expand Down Expand Up @@ -219,6 +221,7 @@ cache_lookup(struct cache *cache, char *key, size_t key_len, void *result)
if (tmp) {
HASH_DELETE(hh, cache->entries, tmp);
tmp->ts = ev_time();
// NOLINTNEXTLINE(clang-analyzer-core.DivideZero): uthash bucket count is never zero
HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp);
*dirty_hack = tmp->data;
} else {
Expand All @@ -241,6 +244,7 @@ cache_key_exist(struct cache *cache, char *key, size_t key_len)
if (tmp) {
HASH_DELETE(hh, cache->entries, tmp);
tmp->ts = ev_time();
// NOLINTNEXTLINE(clang-analyzer-core.DivideZero): uthash bucket count is never zero
HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp);
return 1;
}
Expand Down Expand Up @@ -284,6 +288,7 @@ cache_insert(struct cache *cache, char *key, size_t key_len, void *data)

entry->data = data;
entry->ts = ev_time();
// NOLINTNEXTLINE(clang-analyzer-core.DivideZero): uthash bucket count is never zero
HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry);

if (HASH_COUNT(cache->entries) >= cache->max_entries) {
Expand Down
1 change: 1 addition & 0 deletions src/jconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ read_jconf(const char *file)
conf.user = to_string(value);
} else if (strcmp(name, "plugin") == 0) {
conf.plugin = to_string(value);
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): config strings live for the process lifetime
if (conf.plugin && strlen(conf.plugin) == 0) {
ss_free(conf.plugin);
conf.plugin = NULL;
Expand Down
2 changes: 1 addition & 1 deletion src/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ json_value * json_parse_ex (json_settings * settings,
{
if ( (++ state.ptr) == end)
{
b = 0;
b = 0; /* NOLINT(clang-analyzer-deadcode.DeadStores) */
break;
}

Expand Down
8 changes: 4 additions & 4 deletions src/local.c
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
struct sockaddr_in peer_addr;
socklen_t peer_addr_len = sizeof peer_addr;
if (getpeername(server->fd, (struct sockaddr *)&peer_addr, &peer_addr_len) == 0) {
// NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage): filled by getpeername() on success
LOGI("connection from %s:%hu", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
}
}
Expand Down Expand Up @@ -1276,7 +1277,7 @@ create_remote(listen_ctx_t *listener,
{
struct sockaddr *remote_addr;

int index = rand() % listener->remote_num;
int index = (int)randombytes_uniform((uint32_t)listener->remote_num);
if (addr == NULL) {
remote_addr = listener->remote_addr[index];
} else {
Expand Down Expand Up @@ -1454,7 +1455,6 @@ main(int argc, char **argv)
char *remote_port = NULL;

memset(remote_addr, 0, sizeof(ss_addr_t) * MAX_REMOTE_NUM);
srand(time(NULL));

static struct option long_options[] = {
{ "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT },
Expand Down Expand Up @@ -2059,8 +2059,6 @@ main(int argc, char **argv)
int
_start_ss_local_server(profile_t profile, ss_local_callback callback, void *udata)
{
srand(time(NULL));

char *remote_host = profile.remote_host;
char *local_addr = profile.local_addr;
char *method = profile.method;
Expand Down Expand Up @@ -2130,6 +2128,8 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat

struct sockaddr *remote_addr_tmp[MAX_REMOTE_NUM];
listen_ctx_t listen_ctx;
// fd stays -1 in UDP_ONLY mode but is still passed to the callback below
listen_ctx.fd = -1;
listen_ctx.remote_num = 1;
listen_ctx.remote_addr = remote_addr_tmp;
listen_ctx.remote_addr[0] = (struct sockaddr *)(&storage);
Expand Down
29 changes: 20 additions & 9 deletions src/manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,6 @@ start_server_process(struct manager_ctx *manager, struct server *server)
}

restore_sigchld_after_wait(restore_sigchld, &old_sigchld);
restore_sigchld = 0;

ss_free(pid_path);
ss_free(conf_path);
Expand Down Expand Up @@ -852,11 +851,27 @@ add_server(struct manager_ctx *manager, struct server *server)
return 0;
}

static void
kill_pid_from_file(FILE *f)
{
char buf[16];
int pid;

if (fgets(buf, sizeof(buf), f) == NULL) {
return;
}
buf[strcspn(buf, "\r\n")] = '\0';
// Reject malformed pid file content instead of signaling a garbage pid
if (ss_parse_int(buf, 1, INT_MAX, &pid) == 0) {
kill(pid, SIGTERM);
}
}

static void
kill_server(char *prefix, char *pid_file)
{
char *path = NULL;
int pid, path_size = strlen(prefix) + strlen(pid_file) + 2;
int path_size = strlen(prefix) + strlen(pid_file) + 2;
path = ss_malloc(path_size);
snprintf(path, path_size, "%s/%s", prefix, pid_file);
FILE *f = fopen(path, "r");
Expand All @@ -867,9 +882,7 @@ kill_server(char *prefix, char *pid_file)
ss_free(path);
return;
}
if (fscanf(f, "%d", &pid) != EOF) {
kill(pid, SIGTERM);
}
kill_pid_from_file(f);
fclose(f);
remove(path);
ss_free(path);
Expand All @@ -879,7 +892,7 @@ static void
stop_server(char *prefix, char *port)
{
char *path = NULL;
int pid, path_size = strlen(prefix) + strlen(port) + 20;
int path_size = strlen(prefix) + strlen(port) + 20;
path = ss_malloc(path_size);
snprintf(path, path_size, "%s/.shadowsocks_%s.pid", prefix, port);
FILE *f = fopen(path, "r");
Expand All @@ -890,9 +903,7 @@ stop_server(char *prefix, char *port)
ss_free(path);
return;
}
if (fscanf(f, "%d", &pid) != EOF) {
kill(pid, SIGTERM);
}
kill_pid_from_file(f);
fclose(f);
ss_free(path);
}
Expand Down
4 changes: 1 addition & 3 deletions src/redir.c
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ accept_cb(EV_P_ ev_io *w, int revents)
setsockopt(serverfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
#endif

int index = rand() % listener->remote_num;
int index = (int)randombytes_uniform((uint32_t)listener->remote_num);
struct sockaddr *remote_addr = listener->remote_addr[index];

int protocol = IPPROTO_TCP;
Expand Down Expand Up @@ -891,8 +891,6 @@ signal_cb(EV_P_ ev_signal *w, int revents)
int
main(int argc, char **argv)
{
srand(time(NULL));

int i, c;
int pid_flags = 0;
int mptcp = 0;
Expand Down
1 change: 1 addition & 0 deletions src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ connect_to_remote(EV_P_ struct addrinfo *res,
char ipstr[INET6_ADDRSTRLEN];
memset(ipstr, 0, INET6_ADDRSTRLEN);

// NOLINTNEXTLINE(clang-analyzer-core.NullDereference): callers always set res->ai_addr
if (res->ai_addr->sa_family == AF_INET) {
struct sockaddr_in s;
memcpy(&s, res->ai_addr, sizeof(struct sockaddr_in));
Expand Down
5 changes: 2 additions & 3 deletions src/tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@ static void
free_server(server_t *server)
{
if (server->remote != NULL) {
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): back-pointers are cleared symmetrically on free
server->remote->server = NULL;
}
if (server->e_ctx != NULL) {
Expand Down Expand Up @@ -774,7 +775,7 @@ accept_cb(EV_P_ ev_io *w, int revents)
setsockopt(serverfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
#endif

int index = rand() % listener->remote_num;
int index = (int)randombytes_uniform((uint32_t)listener->remote_num);
struct sockaddr *remote_addr = listener->remote_addr[index];

int protocol = IPPROTO_TCP;
Expand Down Expand Up @@ -924,8 +925,6 @@ plugin_watcher_cb(EV_P_ ev_io *w, int revents)
int
main(int argc, char **argv)
{
srand(time(NULL));

int i, c;
int pid_flags = 0;
int mptcp = 0;
Expand Down
10 changes: 5 additions & 5 deletions src/udprelay.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,11 @@ get_addr_str(const struct sockaddr *sa, bool has_port)

int addr_len = strlen(addr);
int port_len = strlen(port);
memcpy(s, addr, addr_len);
// s is a zeroed static buffer large enough for addr + ':' + port
memcpy(s, addr, addr_len); // NOLINT(bugprone-not-null-terminated-result)

if (has_port) {
memcpy(s + addr_len + 1, port, port_len);
memcpy(s + addr_len + 1, port, port_len); // NOLINT(bugprone-not-null-terminated-result)
s[addr_len] = ':';
}

Expand Down Expand Up @@ -1023,8 +1024,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
LOGE("[udp] unable to get dest addr");
goto CLEAN_UP;
}

src_addr_len = msg.msg_namelen;
#else
ssize_t r;
r = recvfrom(server_ctx->fd, buf->data, buf_size,
Expand Down Expand Up @@ -1180,7 +1179,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
}
addr_header[addr_header_len++] = 3;
addr_header[addr_header_len++] = host_len;
memcpy(addr_header + addr_header_len, host, host_len);
// addr_header is a binary protocol buffer, not a C string
memcpy(addr_header + addr_header_len, host, host_len); // NOLINT(bugprone-not-null-terminated-result)
addr_header_len += host_len;
}
memcpy(addr_header + addr_header_len, &port_net_num, 2);
Expand Down
21 changes: 7 additions & 14 deletions src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,27 +570,21 @@ get_default_conf(void)
{
#ifndef __MINGW32__
static char sysconf[] = "/etc/shadowsocks-libev/config.json";
static char *userconf = NULL;
static int buf_size = 0;
static char userconf[PATH_MAX];
char *conf_home;

conf_home = getenv("XDG_CONFIG_HOME");

// Memory of userconf only gets allocated once, and will not be
// freed. It is used as static buffer.
if (!conf_home) {
if (buf_size == 0) {
buf_size = 50 + strlen(getenv("HOME"));
userconf = malloc(buf_size);
// HOME may be unset (e.g. when started by an init system)
const char *home = getenv("HOME");
if (home == NULL) {
return sysconf;
}
snprintf(userconf, buf_size, "%s%s", getenv("HOME"),
snprintf(userconf, sizeof(userconf), "%s%s", home,
"/.config/shadowsocks-libev/config.json");
} else {
if (buf_size == 0) {
buf_size = 50 + strlen(conf_home);
userconf = malloc(buf_size);
}
snprintf(userconf, buf_size, "%s%s", conf_home,
snprintf(userconf, sizeof(userconf), "%s%s", conf_home,
"/shadowsocks-libev/config.json");
}

Expand All @@ -599,7 +593,6 @@ get_default_conf(void)
return userconf;

// If not, fall back to the system-wide config.
free(userconf);
return sysconf;
#else
return "config.json";
Expand Down
2 changes: 1 addition & 1 deletion src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ int ss_isnumeric(const char *s);
int ss_parse_int(const char *s, int min_value, int max_value, int *out);
int ss_parse_uint16_port(const char *s, uint16_t *out);
int run_as(const char *user);
void FATAL(const char *msg);
void FATAL(const char *msg) __attribute__((noreturn));
void usage(void);
void daemonize(const char *path);
char *ss_strndup(const char *s, size_t n);
Expand Down
Loading