commit 25d79ad7184c2e51d840a50427590906803ea037
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-05-29T22:29:54Z |
| subject | Update fuzzer, removed default config |
commit 25d79ad7184c2e51d840a50427590906803ea037
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-05-29T22:29:54Z
Update fuzzer, removed default config
---
Dockerfile | 4 +-
examples/tinyproxy.conf | 37 +++++++
fuzz/Makefile | 20 ++--
fuzz/README.md | 8 ++
fuzz/fuzz_file_conf.c | 112 +++------------------
fuzz/fuzz_proxy_v2_build.c | 2 +-
fuzz/seeds/file_conf/tinyproxy.conf | 3 +
src/file_conf.c | 190 ++++++++++++++++++++++++++++++------
src/file_conf.h | 10 +-
src/route.c | 1 +
src/tinyproxy.c | 15 +--
tinyproxy.conf | 38 --------
12 files changed, 249 insertions(+), 191 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index a63b63a..deeaf3f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,12 +18,10 @@ RUN --mount=type=cache,target=/var/cache/apk,sharing=locked \
COPY tests ./tests
RUN make test STATIC=1
-RUN cp /build/tinyproxy.conf /etc/ && cp /build/bin/tinyproxy /usr/bin/
+RUN cp /build/bin/tinyproxy /usr/bin/
FROM scratch
COPY --from=build /build/bin/tinyproxy /usr/bin/tinyproxy
-COPY --from=build /build/tinyproxy.conf /etc/tinyproxy.conf
ENTRYPOINT ["/usr/bin/tinyproxy"]
-CMD ["-c", "/etc/tinyproxy.conf"]
diff --git a/examples/tinyproxy.conf b/examples/tinyproxy.conf
new file mode 100644
index 0000000..f556b7d
--- /dev/null
+++ b/examples/tinyproxy.conf
@@ -0,0 +1,37 @@
+# See man 5 tinyproxy
+# TCP stream forwarding.
+listen tcp :80 tcp 10.0.1.1:80
+listen tcp :443 tcp 10.0.1.1:443 proxy_v2,keep_alive
+
+# TCP # listener to UNIX stream backend.
+listen tcp :12990 unix /tmp/test.sock
+
+# UDP datagram forwarding.
+listen udp :12345 udp 10.0.1.1:12345 idle_timeout=10,connect_timeout=17
+listen udp :19132 udp 10.0.1.1:19132
+
+# UDP # listener to UNIX datagram backend.
+listen udp :13000 unix-dgram /tmp/testd.sock
+
+# Builtin TCP test backends.
+listen tcp :12995 builtin client_addr
+listen tcp :12996 builtin hang idle_timeout=3,connect_timeout=1
+listen tcp :12997 builtin discard
+listen tcp :12998 builtin close
+listen tcp :8080 builtin http-ok
+
+# Builtin UDP test backends.
+listen udp :12995 builtin client_addr
+listen udp :12996 builtin hang
+listen udp :12997 builtin discard
+listen udp :12998 builtin close
+listen udp :8080 builtin http-ok
+
+# Static file TCP backends.
+listen tcp :14000 file examples/http-response.txt
+
+listen unix /tmp/tinyproxy-chain-1.sock file examples/http-response.txt
+listen tcp :13002 unix /tmp/tinyproxy-chain-1.sock
+listen tcp :13003 tcp 127.0.0.1:13002
+listen unix /tmp/tinyproxy-chain-4.sock tcp 127.0.0.1:13003
+listen tcp :14001 unix /tmp/tinyproxy-chain-4.sock
diff --git a/fuzz/Makefile b/fuzz/Makefile
index c6e0acb..fbe7418 100644
--- a/fuzz/Makefile
+++ b/fuzz/Makefile
@@ -31,25 +31,17 @@ all: $(BINS)
$(OUT):
mkdir -p $(OUT)
-$(OUT)/fuzz_proxy_v2_build: \
- fuzz_proxy_v2_build.c \
- $(SRC_DIR)/proxy_proto_v2.c \
- | $(OUT)
+$(OUT)/fuzz_proxy_v2_build: fuzz_proxy_v2_build.c $(PROJECT_SRCS)
$(CC) $(CFLAGS) $(FUZZ_CFLAGS) $(CPPFLAGS) \
- $(SRC_DIR)/proxy_proto_v2.c \
- fuzz_proxy_v2_build.c \
+ -I$(SRC_DIR) \
+ $^ \
-o $@ \
$(LDFLAGS) $(FUZZ_LDFLAGS) $(LDLIBS)
-$(OUT)/fuzz_file_conf: \
- fuzz_file_conf.c \
- $(SRC_DIR)/file_conf.c \
- $(SRC_DIR)/klog.c \
- | $(OUT)
+$(OUT)/fuzz_file_conf: fuzz_file_conf.c $(PROJECT_SRCS)
$(CC) $(CFLAGS) $(FUZZ_CFLAGS) $(CPPFLAGS) \
- $(SRC_DIR)/file_conf.c \
- $(SRC_DIR)/klog.c \
- fuzz_file_conf.c \
+ -I$(SRC_DIR) \
+ $^ \
-o $@ \
$(LDFLAGS) $(FUZZ_LDFLAGS) $(LDLIBS)
diff --git a/fuzz/README.md b/fuzz/README.md
new file mode 100755
index 0000000..7fc1adf
--- /dev/null
+++ b/fuzz/README.md
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+macos
+
+Fuzz for 8 hours
+```
+LIBEVENT_SRC=libevent2/ MAX_TOTAL_TIME=28800 MAX_LEN=65536 CC="$(brew --prefix llvm)/bin/clang" caffeinate make -C fuzz run-file_conf
+```
diff --git a/fuzz/fuzz_file_conf.c b/fuzz/fuzz_file_conf.c
index 24351b8..93cb1d4 100644
--- a/fuzz/fuzz_file_conf.c
+++ b/fuzz/fuzz_file_conf.c
@@ -5,119 +5,33 @@
#include <unistd.h>
#include <errno.h>
+#include "route.h"
#include "file_conf.h"
-static void check_endpoint(const struct endpoint *ep)
-{
- switch (ep->kind) {
- case ENDPOINT_INET:
- if (ep->port == 0) {
- abort();
- }
- if (ep->host[0] == '\0') {
- abort();
- }
- break;
-
- case ENDPOINT_UNIX:
- case ENDPOINT_UNIX_DGRAM:
- if (ep->path[0] == '\0') {
- abort();
- }
- break;
-
- default:
- abort();
- }
-}
-
-static void check_route(const struct route *r)
-{
- check_endpoint(&r->listen);
- check_endpoint(&r->upstream);
-
- switch (r->proto) {
- case PROTO_TCP:
- case PROTO_UDP:
- break;
- default:
- abort();
- }
-
- if (r->line_no == 0) {
- abort();
- }
-
- if (r->opts.idle_timeout_sec <= 0) {
- abort();
- }
-
- if (r->opts.connect_timeout_sec <= 0) {
- abort();
- }
-}
-
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
- /*
- * Keep the fuzzer fast. file_conf.c has MAX_LINE_LEN=512, so this is
- * already enough to exercise normal, multiline, and overlong cases.
- */
- if (size > 8192) {
- return 0;
- }
-
- char path[] = "/tmp/tinyproxy-file-conf-fuzz-XXXXXX";
- int fd = mkstemp(path);
- if (fd < 0) {
- return 0;
- }
-
- size_t off = 0;
- while (off < size) {
- ssize_t n = write(fd, data + off, size - off);
- if (n < 0) {
- if (errno == EINTR) {
- continue;
- }
-
- close(fd);
- unlink(path);
- return 0;
- }
-
- if (n == 0) {
- break;
- }
-
- off += (size_t)n;
- }
-
- close(fd);
-
struct route *routes = NULL;
size_t count = 0;
- int rc = load_routes_from_file(path, &routes, &count);
-
- unlink(path);
+ int rc = load_routes_from_text(
+ "fuzzer",
+ (const char *)data,
+ size,
+ &routes,
+ &count
+ );
if (rc == 0) {
if (count > 0 && routes == NULL) {
abort();
}
- for (size_t i = 0; i < count; i++) {
- check_route(&routes[i]);
- }
+ free(routes);
+ return 0;
+ }
- free_routes(routes);
- } else {
- /*
- * On failure, load_routes_from_file() should not transfer ownership.
- * Keep this loose because callers should not rely on output values
- * after error unless the API explicitly promises that.
- */
+ if (routes != NULL) {
+ abort();
}
return 0;
diff --git a/fuzz/fuzz_proxy_v2_build.c b/fuzz/fuzz_proxy_v2_build.c
index 2861e51..dc73f59 100644
--- a/fuzz/fuzz_proxy_v2_build.c
+++ b/fuzz/fuzz_proxy_v2_build.c
@@ -4,7 +4,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
-#include "../src/proxy_proto_v2.h"
+#include "proxy_proto_v2.h"
static uint16_t read_u16(const uint8_t *p)
{
diff --git a/fuzz/seeds/file_conf/tinyproxy.conf b/fuzz/seeds/file_conf/tinyproxy.conf
new file mode 100644
index 0000000..3c762a9
--- /dev/null
+++ b/fuzz/seeds/file_conf/tinyproxy.conf
@@ -0,0 +1,3 @@
+# Fuzz seed valid conf
+listen tcp :80 tcp 10.0.1.1:80
+listen tcp :443 tcp 10.0.1.1:443 proxy_v2,keep_alive
diff --git a/src/file_conf.c b/src/file_conf.c
index 2b3f8e1..1d193ba 100644
--- a/src/file_conf.c
+++ b/src/file_conf.c
@@ -7,8 +7,8 @@
#include <limits.h>
#include "klog.h"
-#include "file_conf.h"
#include "endpoint.h"
+#include "file_conf.h"
#define MAX_LINE_LEN 512
#define MAX_ROUTE_FIELDS 32
@@ -47,6 +47,43 @@ static int parse_port(const char *s, uint16_t *out)
return 0;
}
+static bool same_listen_endpoint(
+ const struct endpoint *a,
+ const struct endpoint *b
+) {
+ if (a->proto != b->proto) {
+ return false;
+ }
+
+ switch (a->proto) {
+ case PROTO_TCP:
+ case PROTO_UDP:
+ return strcmp(a->host, b->host) == 0 &&
+ a->port == b->port;
+
+ case PROTO_UNIX_STREAM:
+ case PROTO_UNIX_DGRAM:
+ return strcmp(a->path, b->path) == 0;
+
+ default:
+ return false;
+ }
+}
+
+static int find_conflicting_route(
+ const struct route *routes,
+ size_t count,
+ const struct route *new_route
+) {
+ for (size_t i = 0; i < count; i++) {
+ if (same_listen_endpoint(&routes[i].listen, &new_route->listen)) {
+ return (int)i;
+ }
+ }
+
+ return -1;
+}
+
static int split_host_port(const char *input,
char *host, size_t host_len,
uint16_t *port)
@@ -394,37 +431,65 @@ int parse_route_line(char *line, struct route *route)
return 0;
}
-int load_routes_from_file(const char *path, struct route **routes_out, size_t *count_out)
-{
- FILE *fp = fopen(path, "r");
- if (!fp) {
- return -errno;
- }
-
+int load_routes_from_text(
+ const char *name,
+ const char *data,
+ size_t data_len,
+ struct route **routes_out,
+ size_t *count_out
+) {
struct route *routes = NULL;
size_t count = 0;
size_t cap = 0;
- char buf[MAX_LINE_LEN];
+ size_t pos = 0;
unsigned int line_no = 0;
- while (fgets(buf, sizeof(buf), fp)) {
+ *routes_out = NULL;
+ *count_out = 0;
+
+ while (pos < data_len) {
line_no++;
- // Reject overlong lines.
- if (!strchr(buf, '\n') && !feof(fp)) {
- fclose(fp);
+ const char *line_start = data + pos;
+ const void *nlp = memchr(line_start, '\n', data_len - pos);
+
+ size_t raw_len;
+ if (nlp) {
+ raw_len = (const char *)nlp - line_start;
+ pos += raw_len + 1;
+ } else {
+ raw_len = data_len - pos;
+ pos = data_len;
+ }
+
+ /* Handle CRLF input nicely. */
+ if (raw_len > 0 && line_start[raw_len - 1] == '\r') {
+ raw_len--;
+ }
+
+ /*
+ * Need room for NUL.
+ *
+ * This keeps the same basic safety property as the fgets()
+ * version: no parser call ever sees an overlarge line.
+ */
+ if (raw_len >= MAX_LINE_LEN) {
free(routes);
return -E2BIG;
}
+ char buf[MAX_LINE_LEN];
+ memcpy(buf, line_start, raw_len);
+ buf[raw_len] = '\0';
+
char *line = trim(buf);
if (*line == '\0' || *line == '#') {
continue;
}
- // Strip inline comments.
+ /* Strip inline comments. */
char *hash = strchr(line, '#');
if (hash) {
*hash = '\0';
@@ -436,9 +501,14 @@ int load_routes_from_file(const char *path, struct route **routes_out, size_t *c
if (count == cap) {
size_t new_cap = cap ? cap * 2 : 8;
+
+ if (new_cap > SIZE_MAX / sizeof(*routes)) {
+ free(routes);
+ return -EOVERFLOW;
+ }
+
struct route *new_routes = realloc(routes, new_cap * sizeof(*routes));
if (!new_routes) {
- fclose(fp);
free(routes);
return -ENOMEM;
}
@@ -453,34 +523,96 @@ int load_routes_from_file(const char *path, struct route **routes_out, size_t *c
struct route *r = &routes[count];
if (parse_route_line(line, r) != 0) {
LOG_ERROR("invalid route config",
- "path", _LOGV(path),
- "line", _LOGV(line_no),
- "text", _LOGV(original));
- fclose(fp);
+ "name", _LOGV(name ? name : "<memory>"),
+ "line", _LOGV(line_no),
+ "text", _LOGV(original)
+ );
+
free(routes);
return -EINVAL;
}
r->line_no = line_no;
+
+ int conflict_idx = find_conflicting_route(routes, count, r);
+ if (conflict_idx >= 0) {
+ const struct route *old = &routes[conflict_idx];
+
+ LOG_ERROR("conflicting route config",
+ "name", _LOGV(name ? name : "<memory>"),
+ "line", _LOGV(line_no),
+ "conflicts_with_line", _LOGV(old->line_no),
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "text", _LOGV(original)
+ );
+
+ free(routes);
+ return -EADDRINUSE;
+ }
+
count++;
}
- if (ferror(fp)) {
+ *routes_out = routes;
+ *count_out = count;
+
+ return 0;
+}
+
+int load_routes_from_file(const char *path, struct route **routes_out, size_t *count_out)
+{
+ FILE *fp = fopen(path, "rb");
+ if (!fp) {
+ return -errno;
+ }
+
+ if (fseek(fp, 0, SEEK_END) != 0) {
int err = errno;
fclose(fp);
- free(routes);
return -err;
}
- fclose(fp);
+ long file_size = ftell(fp);
+ if (file_size < 0) {
+ int err = errno;
+ fclose(fp);
+ return -err;
+ }
- *routes_out = routes;
- *count_out = count;
+ if (fseek(fp, 0, SEEK_SET) != 0) {
+ int err = errno;
+ fclose(fp);
+ return -err;
+ }
- return 0;
-}
+ char *data = NULL;
-void free_routes(struct route *routes)
-{
- free(routes);
+ if (file_size > 0) {
+ data = malloc((size_t)file_size);
+ if (!data) {
+ fclose(fp);
+ return -ENOMEM;
+ }
+
+ size_t nread = fread(data, 1, (size_t)file_size, fp);
+ if (nread != (size_t)file_size) {
+ int err = ferror(fp) ? errno : EIO;
+ free(data);
+ fclose(fp);
+ return -err;
+ }
+ }
+
+ fclose(fp);
+
+ int rc = load_routes_from_text(
+ path,
+ data ? data : "",
+ (size_t)file_size,
+ routes_out,
+ count_out
+ );
+
+ free(data);
+ return rc;
}
diff --git a/src/file_conf.h b/src/file_conf.h
index 5b7baad..fd0bffe 100644
--- a/src/file_conf.h
+++ b/src/file_conf.h
@@ -7,7 +7,15 @@
#include "route.h"
int parse_route_line(char *line, struct route *route);
+
+int load_routes_from_text(
+ const char *name,
+ const char *data,
+ size_t data_len,
+ struct route **routes_out,
+ size_t *count_out
+);
+
int load_routes_from_file(const char *path, struct route **routes_out, size_t *count_out);
-void free_routes(struct route *routes);
#endif
diff --git a/src/route.c b/src/route.c
index aa368b9..fa517c0 100644
--- a/src/route.c
+++ b/src/route.c
@@ -1,4 +1,5 @@
#include <stdio.h>
+#include <stdlib.h>
#include <errno.h>
#include "worker.h"
diff --git a/src/tinyproxy.c b/src/tinyproxy.c
index 1486884..f1731c9 100644
--- a/src/tinyproxy.c
+++ b/src/tinyproxy.c
@@ -46,13 +46,10 @@ int main(int argc, char **argv)
{
prevent_socket_write_from_killing_process();
- const char *conf_path = "tinyproxy.conf";
int opt;
int exit_code = 1;
int rc;
- int conf_path_set = 0;
-
const char **inline_routes = NULL;
size_t inline_route_count = 0;
size_t inline_route_cap = 0;
@@ -61,6 +58,7 @@ int main(int argc, char **argv)
int wsa_started = 0;
#endif
+ const char *conf_path = NULL;
struct route *routes = NULL;
size_t route_count = 0;
@@ -80,7 +78,6 @@ int main(int argc, char **argv)
return 2;
}
conf_path = optarg;
- conf_path_set = 1;
break;
case 'L':
@@ -126,7 +123,13 @@ int main(int argc, char **argv)
}
}
- if (conf_path_set && inline_route_count > 0) {
+ if(conf_path == NULL && inline_route_count == 0) {
+ usage(stdout, argv[0]);
+ free(inline_routes);
+ return 0;
+ }
+
+ if (conf_path && inline_route_count > 0) {
LOG_ERROR("-c and -L cannot be used together");
usage(stderr, argv[0]);
free(inline_routes);
@@ -269,7 +272,7 @@ out:
event_base_free(base);
}
- free_routes(routes);
+ free(routes);
free(inline_routes);
#ifdef _WIN32
diff --git a/tinyproxy.conf b/tinyproxy.conf
deleted file mode 100644
index 4aea92a..0000000
--- a/tinyproxy.conf
+++ /dev/null
@@ -1,38 +0,0 @@
-# See man 5 tinyproxy
-# TCP stream forwarding.
-# listen tcp :80 tcp 10.0.1.1:80
-# listen tcp :443 tcp 10.0.1.1:443 proxy_v2,keep_alive
-
-# TCP # listener to UNIX stream backend.
-# listen tcp :12990 unix /tmp/test.sock
-
-# UDP datagram forwarding.
-# listen udp :12345 udp 10.0.1.1:12345 idle_timeout=10,connect_timeout=17
-# listen udp :19132 udp 10.0.1.1:19132
-
-# UDP # listener to UNIX datagram backend.
-# listen udp :13000 unix-dgram /tmp/testd.sock
-
-# Builtin TCP test backends.
-# listen tcp :12995 builtin client_addr
-# listen tcp :12996 builtin hang idle_timeout=3,connect_timeout=1
-# listen tcp :12997 builtin discard
-# listen tcp :12998 builtin close
-# listen tcp :8080 builtin http-ok
-
-# Builtin UDP test backends.
-# listen udp :12995 builtin client_addr
-# listen udp :12996 builtin hang
-# listen udp :12997 builtin discard
-# listen udp :12998 builtin close
-# listen udp :8080 builtin http-ok
-
-# Static file TCP backends.
-# listen tcp :14000 file examples/http-response.txt
-# listen tcp :14001 file /does/not/exist
-
-# listen unix /tmp/tinyproxy-chain-1.sock file examples/http-response.txt
-# listen tcp :13002 unix /tmp/tinyproxy-chain-1.sock
-# listen tcp :13003 tcp 127.0.0.1:13002
-# listen unix /tmp/tinyproxy-chain-4.sock tcp 127.0.0.1:13003
-# listen tcp :14001 unix /tmp/tinyproxy-chain-4.sock