summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHauke Mehrtens2026-05-03 20:51:24 +0000
committerHauke Mehrtens2026-05-03 23:59:50 +0000
commit05406f70d05cae77de4e7e4164c79f9c14c2a485 (patch)
tree844b37aa89be47c58650be15e305f88ea7998b86
parent07f0afb3bf9160b90111009829676da1758060ff (diff)
downloaduhttpd-05406f70d05cae77de4e7e4164c79f9c14c2a485.tar.gz
file: scan all entries when matching If-Match / If-None-Match
The list-walking logic in uh_file_if_match and uh_file_if_none_match was broken in two ways: 1. The for-loop guard recomputed strlen(hdr) on every iteration, but the body wrote a NUL terminator into the buffer at each separator. After the first comma or space, strlen returns the length of the truncated prefix, so the loop exits before any subsequent tags are reached. 2. After NUL-terminating the current tag the body did 'hdr[i++] = 0; p = &hdr[i];', and the for-loop's own i++ then advanced past the first character of the next tag. In addition strcmp(p, ...) was only run on non-separator iterations, so it never compared a fully NUL-terminated tag against the target in the general case. Snapshot the header length once, iterate up to and including the trailing NUL, and compare each accumulated tag at every separator boundary. This makes If-Match / If-None-Match honor every entry in the list, so cache validation produces the correct 304 / 412 response when the matching tag is not the first one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
-rw-r--r--file.c43
1 files changed, 23 insertions, 20 deletions
diff --git a/file.c b/file.c
index d93ee36..dd0f881 100644
--- a/file.c
+++ b/file.c
@@ -377,20 +377,21 @@ static bool uh_file_if_match(struct client *cl, struct stat *s)
char buf[128];
const char *tag = uh_file_mktag(s, buf, sizeof(buf));
char *hdr = uh_file_header(cl, HDR_IF_MATCH);
+ int hlen, i;
char *p;
- int i;
if (!hdr)
return true;
+ hlen = strlen(hdr);
p = &hdr[0];
- for (i = 0; i < strlen(hdr); i++)
+ for (i = 0; i <= hlen; i++)
{
- if ((hdr[i] == ' ') || (hdr[i] == ',')) {
- hdr[i++] = 0;
- p = &hdr[i];
- } else if (!strcmp(p, "*") || !strcmp(p, tag)) {
- return true;
+ if (hdr[i] == ' ' || hdr[i] == ',' || hdr[i] == '\0') {
+ hdr[i] = 0;
+ if (*p && (!strcmp(p, "*") || !strcmp(p, tag)))
+ return true;
+ p = &hdr[i + 1];
}
}
@@ -418,25 +419,27 @@ static int uh_file_if_none_match(struct client *cl, struct stat *s)
char buf[128];
const char *tag = uh_file_mktag(s, buf, sizeof(buf));
char *hdr = uh_file_header(cl, HDR_IF_NONE_MATCH);
+ int hlen, i;
char *p;
- int i;
if (!hdr)
return true;
+ hlen = strlen(hdr);
p = &hdr[0];
- for (i = 0; i < strlen(hdr); i++) {
- if ((hdr[i] == ' ') || (hdr[i] == ',')) {
- hdr[i++] = 0;
- p = &hdr[i];
- } else if (!strcmp(p, "*") || !strcmp(p, tag)) {
- if ((cl->request.method == UH_HTTP_MSG_GET) ||
- (cl->request.method == UH_HTTP_MSG_HEAD))
- uh_file_response_304(cl, s);
- else
- uh_file_response_412(cl);
-
- return false;
+ for (i = 0; i <= hlen; i++) {
+ if (hdr[i] == ' ' || hdr[i] == ',' || hdr[i] == '\0') {
+ hdr[i] = 0;
+ if (*p && (!strcmp(p, "*") || !strcmp(p, tag))) {
+ if ((cl->request.method == UH_HTTP_MSG_GET) ||
+ (cl->request.method == UH_HTTP_MSG_HEAD))
+ uh_file_response_304(cl, s);
+ else
+ uh_file_response_412(cl);
+
+ return false;
+ }
+ p = &hdr[i + 1];
}
}