mirror of
https://github.com/apache/httpd.git
synced 2026-10-23 22:49:01 +03:00
mod_sed: Fix handling of malformed scripts:
* modules/filters/sed0.c (fcomp): Reject { nested more than SED_DEPTH
deep, and an s or y command with no delimiter.
(ycomp): Index the translation table by the unsigned value of each
byte.
(comple): Return NULL from a failed compile without storing circf.
(address): Reject a line number too large for apr_int64_t.
* test/modules/filters/test_003_sed.py (test_filters_003_37,
test_filters_003_38, test_filters_003_42, test_filters_003_43,
test_filters_003_44, test_filters_003_46, test_filters_003_47): New
tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitHub: PR #791
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1938965 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -295,6 +295,10 @@ swit:
|
||||
case '{':
|
||||
commands->rep->command = BCOM;
|
||||
commands->rep->negfl = !(commands->rep->negfl);
|
||||
if (commands->depth >= SED_DEPTH) {
|
||||
command_errf(commands, SEDERR_TMOMES);
|
||||
return -1;
|
||||
}
|
||||
commands->cmpend[commands->depth++] = &commands->rep->lb1;
|
||||
commands->rep = alloc_reptr(commands);
|
||||
commands->rep->ad1 = p;
|
||||
@@ -528,6 +532,10 @@ jtcommon:
|
||||
|
||||
case 's':
|
||||
commands->rep->command = SCOM;
|
||||
if (*commands->cp == '\0') {
|
||||
command_errf(commands, SEDERR_CGMES, commands->linebuf);
|
||||
return -1;
|
||||
}
|
||||
commands->sseof = *commands->cp++;
|
||||
commands->rep->re1 = p;
|
||||
p = comple(commands, &compargs, (char *) 0, commands->rep->re1,
|
||||
@@ -645,6 +653,10 @@ jtcommon:
|
||||
|
||||
case 'y':
|
||||
commands->rep->command = YCOM;
|
||||
if (*commands->cp == '\0') {
|
||||
command_errf(commands, SEDERR_CGMES, commands->linebuf);
|
||||
return -1;
|
||||
}
|
||||
commands->sseof = *commands->cp++;
|
||||
commands->rep->re1 = p;
|
||||
p = ycomp(commands, commands->rep->re1);
|
||||
@@ -839,8 +851,13 @@ static char *address(sed_commands_t *commands, char *expbuf,
|
||||
rcp = commands->cp;
|
||||
lno = 0;
|
||||
|
||||
while(*rcp >= '0' && *rcp <= '9')
|
||||
lno = lno*10 + *rcp++ - '0';
|
||||
while(*rcp >= '0' && *rcp <= '9') {
|
||||
if (lno > (APR_INT64_MAX - (*rcp - '0')) / 10) {
|
||||
*status = APR_EGENERAL;
|
||||
return NULL;
|
||||
}
|
||||
lno = lno*10 + (*rcp++ - '0');
|
||||
}
|
||||
|
||||
if(rcp > commands->cp) {
|
||||
if (expbuf > &commands->respace[RESIZE-3]) {
|
||||
@@ -954,7 +971,7 @@ static char *ycomp(sed_commands_t *commands, char *expbuf)
|
||||
sp++;
|
||||
c = '\n';
|
||||
}
|
||||
cint = (int) c;
|
||||
cint = (unsigned char) c;
|
||||
if((ep[cint] = *tsp++) == '\\' && *tsp == 'n') {
|
||||
ep[cint] = '\n';
|
||||
tsp++;
|
||||
@@ -996,6 +1013,8 @@ static char *comple(sed_commands_t *commands, sed_comp_args *compargs,
|
||||
char *p;
|
||||
|
||||
p = sed_compile(commands, compargs, ep + 1, x3, x4);
|
||||
if(p == NULL)
|
||||
return NULL;
|
||||
if(p == ep + 1)
|
||||
return(ep);
|
||||
*ep = compargs->circf;
|
||||
|
||||
@@ -452,6 +452,19 @@ class TestSed:
|
||||
assert self.body(env, r"s/\(\(mo\)n\2*\)day/[\2]/").decode() == \
|
||||
"one [mo] two\nthree sunday four\n[mo] monday monday\n"
|
||||
|
||||
# --- blocks -----------------------------------------------------------
|
||||
|
||||
# Blocks nest up to 20 deep. Each } goes in its own directive: the
|
||||
# rest of a line after } is not read.
|
||||
def test_filters_003_37(self, env):
|
||||
assert self.body(env, ["{" * 20 + "s/monday/MON/"] + ["}"] * 20) \
|
||||
.decode() == "one MON two\nthree sunday four\nMON monday monday\n"
|
||||
|
||||
# Any deeper is rejected.
|
||||
def test_filters_003_38(self, env):
|
||||
self.configure(env, ["{" * 21 + "p"] + ["}"] * 21, fails=True)
|
||||
assert "too many {'s" in env.apachectl_stderr, env.apachectl_stderr
|
||||
|
||||
# --- bracket expressions ----------------------------------------------
|
||||
|
||||
# A bracket expression which ends after a range's "-" is rejected, on
|
||||
@@ -486,6 +499,30 @@ class TestSed:
|
||||
b"\\\xe9sunday\xe9s\xe9u\xe9U\xe9"]).decode() \
|
||||
== "one MON two\nthree sUnday four\nMON monday monday\n"
|
||||
|
||||
# An s or y command which ends before its delimiter is rejected.
|
||||
@pytest.mark.parametrize("expr", ["s", "/monday/s", "y", "/monday/y"])
|
||||
def test_filters_003_42(self, env, expr):
|
||||
self.configure(env, expr, fails=True)
|
||||
assert "command garbled" in env.apachectl_stderr, \
|
||||
env.apachectl_stderr
|
||||
|
||||
# y maps bytes with the high bit set like any other.
|
||||
def test_filters_003_43(self, env):
|
||||
self.write_bytes(env, "latin1.html", b"caf\xe9 na\xefve\n")
|
||||
assert self.body(env, [b"y/\xe9\xef/ei/"], path="/latin1.html") == \
|
||||
b"cafe naive\n"
|
||||
|
||||
# A regular expression which no longer fits once the compiled script
|
||||
# has filled its buffer exactly, here with the names of r commands, is
|
||||
# rejected without writing past the buffer. Only a memory checker sees
|
||||
# the difference.
|
||||
def test_filters_003_44(self, env):
|
||||
self.configure(env, [f"r {'A' * 998}"] * 10 + ["r 123456789",
|
||||
"/x/p"],
|
||||
fails=True)
|
||||
assert "too much command text" in env.apachectl_stderr, \
|
||||
env.apachectl_stderr
|
||||
|
||||
# An interval count is rejected as too large however many digits it
|
||||
# has, rather than wrapping around.
|
||||
@pytest.mark.parametrize("expr", [
|
||||
@@ -497,3 +534,14 @@ class TestSed:
|
||||
self.configure(env, expr, fails=True)
|
||||
assert "Range endpoint too large" in env.apachectl_stderr, \
|
||||
env.apachectl_stderr
|
||||
|
||||
# A line number too large for a 64-bit count is rejected rather than
|
||||
# wrapping around; one which fits, but which no document reaches, is
|
||||
# accepted.
|
||||
def test_filters_003_46(self, env):
|
||||
self.configure(env, "99999999999999999999p", fails=True)
|
||||
assert "command garbled" in env.apachectl_stderr, \
|
||||
env.apachectl_stderr
|
||||
|
||||
def test_filters_003_47(self, env):
|
||||
assert self.body(env, "9223372036854775807d").decode() == DOC
|
||||
|
||||
Reference in New Issue
Block a user