-
-
Notifications
You must be signed in to change notification settings - Fork 703
New issue
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
SEGFAULT on macOS with the latest version of watchdog #762
Comments
Thanks for the report. For the history, you are talking about the release v2.0.0. Now, that will be a though one to debug 🤔 Do you think you can reduce the code to ease reproducting the issue? I guess this is releated to some C code that was modified in FSEvents. Maybe @samschott or @CCP-Aporia could have a look at changes introduced in v2.0.0 to see if there is a missing refcount change or something like that? |
I do have an idea how to reduce the code. What I think would repro:
I'll try that later today. |
@bstaletic, could you possibly get the crash report from the CI runner? It should be the latest .crash file in |
I repro'd this locally. this is what crash reporter says: http://vpaste.net/XouKt |
BTW the reproduction occurs when we add |
Thanks @puremourning, that's very helpful! Could you also provide a minimum example for us to reproduce the crash? If I am reading the report correctly, the segfault happens because NULL gets passed to watchdog/src/watchdog_fsevents.c Line 407 in 4bddc32
@CCP-Aporia, would you agree? Now from the FSEvents docs, I would not have expected the event path to ever be null. But maybe the actual issue lies earlier in the code at: watchdog/src/watchdog_fsevents.c Lines 388 to 397 in 4bddc32
This check looks a bit odd to me. Shouldn't it be something like the following instead: /* Convert event flags and paths to Python ints and strings. */
py_event_paths = PyList_New(num_events);
py_event_inodes = PyList_New(num_events);
py_event_flags = PyList_New(num_events);
py_event_ids = PyList_New(num_events);
if (G_NOT(py_event_paths && py_event_inodes && py_event_flags && py_event_ids))
{
Py_XDECREF(py_event_paths);
Py_XDECREF(py_event_inodes);
Py_XDECREF(py_event_flags);
Py_XDECREF(py_event_ids);
return /*NULL*/;
} |
Hey, that's a great a great bug report - thanks a lot for all the details! @samschott - I agree that the crash likely happens because of About the |
@CCP-Aporia, thanks for the explanation. I was wondering which scenario the
It appears like that. I am still confused as to why this would happen and how this is related to raising a RuntimeError in a different thread which handles the stdout from a subprocess. But if there is a scenario where cf_path can be NULL, we clearly need to accommodate that. |
Turns out that it's probably not. The thread that is throwing the exception is actually the same thread that created the
later (triggered by the exception)...
|
TL;DR - I think the crash happens when there are multiple events in the callback, but I don't know why. I am able to reproduce this with a debug version of watchdog and a debugger attached. It seems that it crashes every time it enters this code: if (G_IS_NULL(c_string_ptr)) {
cf_string_length = CFStringGetLength(cf_string_ref);
CFStringGetCString(cf_string_ref, c_string_buff, cf_string_length, 0);
py_string = PyUnicode_FromString(c_string_buff);
} else {
Neither
If it helps, these are the flags for the one that causes it to break:
The only useful other thing that I notice is that it crashes consistently when |
@puremourning, this is very helpful, thanks! It also explains why the segfault didn't show up in our testing: the code block which you show is only called as a backup when Could you try applying this patch and see if it fixes the problem: diff --git a/src/watchdog_fsevents.c b/src/watchdog_fsevents.c
index e9f4e6aced800936af2778cc046fa6382dceae13..92f3ed0c7e067bda0f362cf022d7af2f99c5f69e 100644
--- a/src/watchdog_fsevents.c
+++ b/src/watchdog_fsevents.c
@@ -293,17 +293,27 @@ static void watchdog_pycapsule_destructor(PyObject *ptr)
*/
PyObject * CFString_AsPyUnicode(CFStringRef cf_string_ref)
{
- CFIndex cf_string_length;
- char *c_string_buff = NULL;
+
+ if (G_IS_NULL(cf_string_ref)) {
+ return PyUnicode_FromString("");
+ }
+
const char *c_string_ptr;
PyObject *py_string;
- c_string_ptr = CFStringGetCStringPtr(cf_string_ref, 0);
+ c_string_ptr = CFStringGetCStringPtr(cf_string_ref, kCFStringEncodingUTF8);
if (G_IS_NULL(c_string_ptr)) {
- cf_string_length = CFStringGetLength(cf_string_ref);
- CFStringGetCString(cf_string_ref, c_string_buff, cf_string_length, 0);
- py_string = PyUnicode_FromString(c_string_buff);
+ CFIndex length = CFStringGetLength(cf_string_ref);
+ CFIndex max_length = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
+ char *buffer = (char *)malloc(max_length);
+ if (CFStringGetCString(cf_string_ref, buffer, max_length, kCFStringEncodingUTF8)) {
+ py_string = PyUnicode_FromString(buffer);
+ }
+ else {
+ py_string = PyUnicode_FromString("");
+ }
+ free(buffer);
} else {
py_string = PyUnicode_FromString(c_string_ptr);
} |
applying that patch does indeed resolve the crash for the test case ! thanks. |
Great! I'll submit a PR with the changes. It would still be good to have an example which we can test against, if only to prevent future regressions. But this might be difficult. Do you know which file path caused the segfault? It will likely fulfil two criteria:
The second criterion is pretty vague. According to the documentation for
|
If I'm not mistaken, the watched directory is this one. When our tests are run, jdt.ls creates a directory in there named
That thing is the compiled version of this class. |
Yes, that is it. I can reproduce the segfault with that path. Many thanks for your help in debugging this :) |
…762) * fix converting CFStringRef to PyUnicode * fix handling when creation of PyList fails * added test case for string conversion * declare c_string_ptr in-line * remove trailing slash from tmpdir * use decomposed e-acute in filename for tests this prevents unicode normalisation on HFS+
Thanks everyone for quick analysis and resolution, I love OSS for that kind of actions. ❤️ 🍾 I will release v2.0.1 with the patch. |
Release v2.0.1 online. |
Unfortunately, this will be tough to describe, since I don't have a mac and only my CI is complaining.
I'm using watchdog as a submodule and today tried to update it. One specific test consistently makes pytest segfault.
This is the log from a SEGFAULTING run: https://dev.azure.com/borisstaletic/3ce92110-caa5-4c49-b8c3-44a433da676b/_apis/build/builds/1535/logs/64
The way I'm using watchdog is by talking to, in this case, jdt.ls. When I open this file it sends... these patterns:
Which we then edit, like this: https://github.com/ycm-core/ycmd/blob/master/ycmd/completers/language_server/language_server_completer.py#L590-L591
And finally use that with this handler: https://github.com/ycm-core/ycmd/blob/master/ycmd/completers/language_server/language_server_completer.py#L3219
Any further information, as well as running the CI, I can gladly provide.
The text was updated successfully, but these errors were encountered: