| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Fix crashes in the Tachyon sampling profiler when writing samples with | ||
| invalid container types or tuple sizes. Patch by Maurycy | ||
| Pawłowski-Wieroński. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Expand Up | @@ -48,6 +48,22 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } while (0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #define CHECK_TUPLE_ITEMS(obj, n) do { \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment thread
Copy link
Copy Markdown
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityTruth be told, we reinvent similar check over and over: cpython/Objects/memoryobject.c Lines 217 to 222 in 9a75080
Lines 26 to 33 in 9a75080
Sometimes without a macro: cpython/Modules/_io/stringio.c Lines 922 to 930 in 9a75080
Lines 450 to 465 in 9a75080
Sorry, something went wrong.
All reactions
Comment thread
maurycy marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) < (n)) { \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyErr_Format(PyExc_TypeError, \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #obj " must be a tuple of at least %zd items", \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (Py_ssize_t)(n)); \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } while (0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #define CHECK_LIST(obj) do { \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!PyList_Check(obj)) { \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyErr_SetString(PyExc_TypeError, #obj " must be a list"); \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } while (0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* ============================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * WRITER-SPECIFIC UTILITY HELPERS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * ============================================================================ */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Expand Down Expand Up | @@ -859,8 +875,8 @@ build_frame_stack(BinaryWriter *writer, PyObject *frame_list, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *curr_depth = (stack_depth < MAX_STACK_DEPTH) ? stack_depth : MAX_STACK_DEPTH; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (Py_ssize_t k = 0; k < (Py_ssize_t)*curr_depth; k++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* Use unchecked accessors since we control the data structures */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *frame_info = PyList_GET_ITEM(frame_list, k); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHECK_TUPLE_ITEMS(frame_info, 4); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* Get filename, location, funcname, opcode from FrameInfo using unchecked access */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *filename = PyStructSequence_GET_ITEM(frame_info, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Expand All | @@ -875,20 +891,13 @@ build_frame_stack(BinaryWriter *writer, PyObject *frame_list, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int32_t end_column = LOCATION_NOT_AVAILABLE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (location != Py_None) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHECK_TUPLE_ITEMS(location, 4); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment thread
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Qualitynormalize_location() in Lib/profiling/sampling/collector.py accepts an int location (a bare lineno) and binary_collector.py hands stack_frames straight to write_sample, so this now raises where the other collectors work. Do we want a PyLong_Check(location) case here too, or is that branch in normalize_location() dead and we should drop it instead?
Sorry, something went wrong.
All reactions
Copy link
Copy Markdown
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality@pablogsal I still need some time, but this is a dead path, excercised only by tests. To put aside this PR, do you want me to open a separate PR that removes these?
Sorry, something went wrong.
All reactions
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityYes, please, a separate PR for the integer-location branches and their tests makes sense. Let's keep this one focused on container validation. Thanks!
Sorry, something went wrong.
maurycy reacted with thumbs up emoji
All reactions
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* LocationInfo is a struct sequence or tuple with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * (lineno, end_lineno, column, end_column) */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *lineno_obj = PyTuple_Check(location) ? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyTuple_GET_ITEM(location, 0) : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyStructSequence_GET_ITEM(location, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *end_lineno_obj = PyTuple_Check(location) ? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyTuple_GET_ITEM(location, 1) : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyStructSequence_GET_ITEM(location, 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *column_obj = PyTuple_Check(location) ? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyTuple_GET_ITEM(location, 2) : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyStructSequence_GET_ITEM(location, 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *end_column_obj = PyTuple_Check(location) ? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyTuple_GET_ITEM(location, 3) : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyStructSequence_GET_ITEM(location, 3); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *lineno_obj = PyTuple_GET_ITEM(location, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *end_lineno_obj = PyTuple_GET_ITEM(location, 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *column_obj = PyTuple_GET_ITEM(location, 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *end_column_obj = PyTuple_GET_ITEM(location, 3); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PYLONG_TO_INT32_OR_DEFAULT(lineno_obj, lineno, LOCATION_NOT_AVAILABLE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PYLONG_TO_INT32_OR_DEFAULT(end_lineno_obj, end_lineno, LOCATION_NOT_AVAILABLE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Expand Down Expand Up | @@ -946,6 +955,7 @@ static int | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process_thread_sample(BinaryWriter *writer, PyObject *thread_info, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uint32_t interpreter_id, uint64_t timestamp_us) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHECK_TUPLE_ITEMS(thread_info, 3); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment thread
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityA malformed second thread still counts the first and breaks the writer, so earlier valid samples cannot be finalized. Can we validate before committing the sample?
Sorry, something went wrong.
All reactions
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (writer->total_samples == UINT64_MAX) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writer->state = BINARY_WRITER_LIMIT_REACHED; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyErr_SetString(PyExc_OverflowError, "too many samples for binary format"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Expand All | @@ -955,6 +965,7 @@ process_thread_sample(BinaryWriter *writer, PyObject *thread_info, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *thread_id_obj = PyStructSequence_GET_ITEM(thread_info, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *status_obj = PyStructSequence_GET_ITEM(thread_info, 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *frame_list = PyStructSequence_GET_ITEM(thread_info, 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHECK_LIST(frame_list); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uint64_t thread_id = PyLong_AsUnsignedLongLong(thread_id_obj); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (thread_id == (uint64_t)-1 && PyErr_Occurred()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Expand Down Expand Up | @@ -1037,17 +1048,16 @@ process_thread_sample(BinaryWriter *writer, PyObject *thread_info, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binary_writer_write_sample(BinaryWriter *writer, PyObject *stack_frames, uint64_t timestamp_us) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!PyList_Check(stack_frames)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyErr_SetString(PyExc_TypeError, "stack_frames must be a list"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHECK_LIST(stack_frames); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Py_ssize_t num_interpreters = PyList_GET_SIZE(stack_frames); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (Py_ssize_t i = 0; i < num_interpreters; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *interp_info = PyList_GET_ITEM(stack_frames, i); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHECK_TUPLE_ITEMS(interp_info, 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment thread
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityWe validate as we go, so a bad thread in the middle leaves the earlier threads already written and total_samples bumped even though write_sample raised. Should we walk and validate the whole structure first so this is all-or-nothing? Otherwise the caller has no way to recover from the TypeError.
Sorry, something went wrong.
All reactions
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *interp_id_obj = PyStructSequence_GET_ITEM(interp_info, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *threads = PyStructSequence_GET_ITEM(interp_info, 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHECK_LIST(threads); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment thread
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityThis narrows the hole but does not close it. PyLong_AsLong(status_obj) goes through _PyNumber_Index, so an __index__ that mutates the same list re-enters us while we still hold a cached size and a borrowed thread_info: threads = []
class Evil:
def __index__(self):
del threads[:]
return 0
threads.extend([(1, Evil(), []), (2, 0, [])])
_remote_debugging.BinaryWriter("/tmp/o.bin", 1000, 0, compression=0).write_sample([(0, threads)], 2000)This still segfaults on PyList_GET_ITEM(threads, 1) because del threads[:] frees ob_item. writer_intern_string has the same problem via PyObject_Hash. If we want to claim write_sample is safe against arbitrary input we need strong references to the containers (or to re-read the sizes), not only a type check up front. Happy to take this as a follow-up, but then let's not close the issue with this PR.
Sorry, something went wrong.
maurycy reacted with thumbs up emoji
All reactions
Comment thread
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityThe __index__ repro above is still open: we iterate threads with a cached size and a borrowed thread_info while PyLong_AsLong(status_obj) can run arbitrary code. No rush, but can you tell me if you want to fix it here or in a separate issue? In the second case let's not close gh-157227 with this PR.
Sorry, something went wrong.
All reactions
Comment thread
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityThis still crashes when status.index clears threads: the next iteration uses the cached size and reads freed storage. We need mutation-safe traversal and a regression test.
Sorry, something went wrong.
All reactions
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unsigned long long interp_id_long = PyLong_AsUnsignedLongLong(interp_id_obj); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (interp_id_long == (unsigned long long)-1 && PyErr_Occurred()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Expand Down | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Back | FazBrowse Home | New Git URL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityNit, feel free to ignore: if breaking the writer is intended, can we assert that finalize() raises ValueError after the TypeError?
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.