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
Thread-local storage: handle failure cases #5722
Conversation
a776ea3
to
5b19e95
Compare
Hey Edward, Since you're working on the thread-local stuff, I've hit recently a problem related to freeing a statically allocated error message when a thread is disposed of. I'm testing a small patch although I'm not sure if it could be made better (I'm not yet familiar enough with the code base). Maybe you can consider this for this patch as well. Here's my description of the problem:
And the patch I wrote: index b34aa3abb..3cc003fdd 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -25,7 +25,11 @@ static void set_error_from_buffer(int error_class)
git_error *error = &GIT_GLOBAL->error_t;
git_buf *buf = &GIT_GLOBAL->error_buf;
- error->message = buf->ptr;
+ if (buf->ptr != NULL && buf->ptr[0] == '\0') {
+ error->message = NULL;
+ } else {
+ error->message = buf->ptr;
+ }
error->klass = error_class;
GIT_GLOBAL->last_error = error; It does prevent the crash, but it's probably not the best way to solve it. |
Just to clarify: my doubt on the patch is I don't know if it's a more general problem (more statically allocated strings being placed on the thread-local error) or just a one off. Hence maybe it's appropriate to tackle it on this PR. |
Hi @arroz - very interesting. Can you clarify what you mean when you say that the thread is disposed? Are you kicking off a new thread to run some libgit2 operations and then it's ending, and the thread exists... What is calling |
Hey @ethomson! Yeah: I'm using libgit2 in a macOS app. When a thread sits idle for a few seconds, the system (GCD-related stuff, I suppose) will dispose of it, and that will cause the cb__free_status to run. Here's a stack trace of the crash (the error.c line number is not accurate as I have my patch there commented):
IIRC the error is added to the thread state in the httpclient.c check_certificate function. I think the lastError set by st->certificate contains a statically allocated '\0' that ends up being captured, and causes the crash when git__global_state_cleanup tries to deallocate it. I can't recall all the details, but it was something like that. :) Let me know if you want me to dig further. I just confirmed the crash steps again. The callback needs to be called in a situation where openssl believes the certificate is invalid (meaning "valid" is false when the user callback is called) and the callback must override and return 0 to indicate the certificate is valid. All I have to do after that happens is wait a few seconds, and my app will crash with the above stack trace. |
A little more detail: when it crashes in I don't know yet what puts that in the |
OK, here's the whole sequence:
If the certificate is approved by the callback, nothing else touches So, this is where I'm not sure what to do, since I still don't know the whole mental model around how libgit2 manages errors and the data stored in the thread local. My patch sort of works by chance. I probably should compare the ptr to |
And I forgot to answer part of your original question 😄 Yeah, I'm running a git operation (just a remote ls) on a separate thread (that gets created by Combine and the whole GCD system, but that shouldn't be important). I was testing the certificate handling, and have a web server with a self signed certificate running on a Pi. So the operation runs on that thread, getting the invalid certificate from the Pi which the callback then accepts, and then the thread sits there idle until the OS terminates it about 5-10 seconds later. |
5b19e95
to
fd67d7e
Compare
fd67d7e
to
f385a59
Compare
f385a59
to
2aed803
Compare
2aed803
to
062b3e4
Compare
Thread-local storage data may fail to initialize; in this case, do not try to set the error message into it. When the thread state has not been initialized, return a hardcoded message to that affect.
git_oid_tostr_s could fail if thread-local state initialization fails. In that case, it will now return `NULL`. Callers should check for `NULL` and propagate the failure.
Now that we've reduced the usage of GIT_THREADSTATE, remove it entirely in favor of git_threadstate_get().
062b3e4
to
ebf2991
Compare
We never error-check our "threadstate" thread-local storage, where we keep a few reusable buffers and the thread's current error message. Make sure that we have a threadstate data instance before reading to it or writing from it.
Depends on #5720