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
44 changes: 16 additions & 28 deletions ddtrace/appsec/_iast/_taint_tracking/aspects/aspect_slice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,38 +65,26 @@ build_index_range_map(PyObject* text, TaintRangeRefs& ranges, PyObject* start, P
index_range_map.emplace_back(nullptr);
index++;
}
TaintRangeRefs index_range_map_result;
long start_int = 0;
if (start != nullptr and start != Py_None) {
start_int = PyLong_AsLong(start);
if (start_int < 0) {
start_int = length_text + start_int;
if (start_int < 0) {
start_int = 0;
}
}
}

long stop_int = length_text;
if (stop != nullptr and stop != Py_None) {
stop_int = PyLong_AsLong(stop);
if (stop_int > length_text) {
stop_int = length_text;
} else if (stop_int < 0) {
stop_int = length_text + stop_int;
if (stop_int < 0) {
stop_int = 0;
}
}
PyObject* slice = PySlice_New(
(start != nullptr) ? start : Py_None, (stop != nullptr) ? stop : Py_None, (step != nullptr) ? step : Py_None);
if (slice == nullptr) {
return {};
}

long step_int = 1;
if (step != nullptr and step != Py_None) {
step_int = PyLong_AsLong(step);
Py_ssize_t norm_start, norm_stop, norm_step, slicelength;
if (PySlice_GetIndicesEx(slice, length_text, &norm_start, &norm_stop, &norm_step, &slicelength) < 0) {
Py_DECREF(slice);
return {};
}
Py_DECREF(slice);

for (auto i = start_int; i < stop_int; i += step_int) {
index_range_map_result.emplace_back(index_range_map[i]);
TaintRangeRefs index_range_map_result;
Py_ssize_t map_size = static_cast<Py_ssize_t>(index_range_map.size());
for (Py_ssize_t i = 0; i < slicelength; i++) {
Py_ssize_t idx = norm_start + i * norm_step;
if (idx >= 0 && idx < map_size) {
index_range_map_result.emplace_back(index_range_map[idx]);
}
}

return index_range_map_result;
Expand Down
31 changes: 31 additions & 0 deletions tests/appsec/iast/aspects/test_slice_aspect_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,37 @@ def test_string_slice_negative():
assert result == expected_result


@pytest.mark.parametrize(
"input_str, start_pos, end_pos, step, expected_result",
[
# Regression: negative step triggered OOB read in build_index_range_map
# because start_int=0 < stop_int=len satisfied i<stop for all negative i.
("hello", None, None, -1, "olleh"),
("hello", None, None, -2, "olh"),
("hello", 4, 0, -1, "olle"),
],
)
def test_string_slice_negative_step_oob_regression(
input_str: str, start_pos: object, end_pos: object, step: int, expected_result: str
) -> None:
"""Regression test for out-of-bounds read in slice aspect with negative step."""
result = mod.do_slice(input_str, start_pos, end_pos, step) # pylint: disable=no-member
assert result == expected_result

tainted_input = taint_pyobject(
pyobject=input_str,
source_name="input_str",
source_value="foo",
source_origin=OriginType.PARAMETER,
)
result = mod.do_slice(tainted_input, start_pos, end_pos, step) # pylint: disable=no-member
assert result == expected_result
tainted_ranges = get_tainted_ranges(result)
assert len(tainted_ranges) == 1
assert tainted_ranges[0].start == 0
assert tainted_ranges[0].length == len(expected_result)


@pytest.mark.skip_iast_check_logs
def test_propagate_ranges_with_no_context(caplog):
tainted_input = taint_pyobject(
Expand Down
Loading