MDEV-30432 replace curl rest connector with libcurl usage#5071
MDEV-30432 replace curl rest connector with libcurl usage#5071grooverdan wants to merge 3 commits into
Conversation
- Remove GetRest occurance - Add libcurl feature to ConnectSE - This patch closes MDEV-26727 (tested with Docker)
…nds" This reverts commit 2a78c3e.
There was a problem hiding this comment.
Code Review
This pull request replaces the Casablanca (cpprestsdk) dependency and external curl process calls with direct libcurl integration for the CONNECT storage engine's REST support. The changes involve removing restget.cpp, updating build configurations, and implementing new curl_init, curl_run, and curl_deinit methods within the RESTDEF class. Feedback identifies critical thread-safety concerns regarding the placement of global curl initialization, a dangling pointer bug in DefineAM, and potential null pointer dereferences. Further improvements are suggested for file handling robustness, error reporting, URL buffer sizing, and the removal of unused data structures.
| int RESTDEF::curl_init(PGLOBAL g) | ||
| { | ||
| CURLcode curl_res = curl_global_init(CURL_GLOBAL_ALL); | ||
| if (curl_res != CURLE_OK) | ||
| { | ||
| snprintf(g->Message, sizeof(g->Message), | ||
| "unable to initialize curl library, " | ||
| "curl returned this error code: %u " | ||
| "with the following error message: %s", | ||
| curl_res, curl_easy_strerror(curl_res)); | ||
| return 1; | ||
| } | ||
| curl_inited = true; | ||
| return 0; | ||
| } | ||
|
|
||
|
|
||
| /***********************************************************************/ | ||
| /* curl_deinit: Cleanup curl */ | ||
| /***********************************************************************/ | ||
| void RESTDEF::curl_deinit() | ||
| { | ||
| if (curl_inited) | ||
| { | ||
| curl_global_cleanup(); | ||
| curl_inited = false; | ||
| } | ||
| } |
There was a problem hiding this comment.
The functions curl_global_init and curl_global_cleanup are not thread-safe and are intended to be called exactly once per process. Calling them within curl_init and curl_deinit (which is called by the RESTDEF destructor) means they will be executed every time a REST table is defined or queried. In a multi-threaded environment like MariaDB, this will lead to race conditions, memory corruption, or crashes if multiple threads access REST tables simultaneously. These calls should be moved to the global plugin initialization and de-initialization routines.
| return true; | ||
| } else switch (n) { | ||
| case 1: Tdp = new (g) JSONDEF; break; | ||
| Fn= filename; |
There was a problem hiding this comment.
Dangling pointer: Fn is a member of the RESTDEF class, but it is being assigned the address of filename, which is a local array on the stack of the DefineAM function. When DefineAM returns, Fn will point to invalid memory. This will cause crashes or data corruption when the filename is accessed later. Use PlugDup to allocate a persistent copy of the path.
Fn= PlugDup(g, filename);| if (Uri) | ||
| { | ||
| if (*Uri == '/' || Http[strlen(Http) - 1] == '/') | ||
| my_snprintf(buf, sizeof(buf)-1, "%s%s", Http, Uri); | ||
| else | ||
| my_snprintf(buf, sizeof(buf)-1, "%s/%s", Http, Uri); | ||
| } | ||
| else | ||
| my_snprintf(buf, sizeof(buf)-1, "%s", Http); |
There was a problem hiding this comment.
Potential crash: Http is not checked for NULL before being used in strlen(Http) and my_snprintf. If the Http option is missing from the table definition, this will cause a null pointer dereference. Additionally, if Http is an empty string, Http[strlen(Http) - 1] will result in an out-of-bounds access.
if (!Http || !*Http)
{
snprintf(g->Message, sizeof(g->Message), "HTTP URL is missing or empty.");
return 1;
}
if (Uri && *Uri)
{
if (*Uri == '/' || Http[strlen(Http) - 1] == '/')
my_snprintf(buf, sizeof(buf), "%s%s", Http, Uri);
else
my_snprintf(buf, sizeof(buf), "%s/%s", Http, Uri);
}
else
my_snprintf(buf, sizeof(buf), "%s", Http);| FILE *f= fopen(Fn, "wb"); | ||
| if ((curl_res= curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf)) != | ||
| CURLE_OK || | ||
| (curl_res= curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, | ||
| NULL)) != | ||
| CURLE_OK || | ||
| (curl_res= curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) f)) != | ||
| CURLE_OK || | ||
| (curl_res = curl_easy_setopt(curl, CURLOPT_URL, buf)) != CURLE_OK || | ||
| (curl_res = curl_easy_perform(curl)) != CURLE_OK || | ||
| (curl_res = curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, | ||
| &http_code)) != CURLE_OK) | ||
| { | ||
| curl_easy_cleanup(curl); | ||
| if (curl_res) | ||
| { | ||
| snprintf(g->Message, sizeof(g->Message), | ||
| "curl returned this error code: %u " | ||
| "with the following error message: %s", curl_res, | ||
| curl_errbuf[0] ? curl_errbuf : curl_easy_strerror(curl_res)); | ||
| return 1; | ||
| } | ||
| } | ||
| curl_easy_cleanup(curl); | ||
| fclose(f); |
There was a problem hiding this comment.
Multiple issues with file handling:
- The return value of
fopenis not checked. If it fails,fisNULL, which will cause a crash infclose(f)at line 281 and potentially inside libcurl ifCURLOPT_WRITEDATAis used with aNULLpointer and the default write function. - If any
curl_easy_setoptorcurl_easy_performcall fails, the function returns without callingfclose(f), resulting in a file descriptor leak. - If
fopenfails, the error should be reported and the function should return early.
FILE *f = fopen(Fn, "wb");
if (!f)
{
snprintf(g->Message, sizeof(g->Message), "Cannot open file %s for writing.", Fn);
curl_easy_cleanup(curl);
return 1;
}
if ((curl_res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf)) != CURLE_OK ||
(curl_res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL)) != CURLE_OK ||
(curl_res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)f)) != CURLE_OK ||
(curl_res = curl_easy_setopt(curl, CURLOPT_URL, buf)) != CURLE_OK ||
(curl_res = curl_easy_perform(curl)) != CURLE_OK ||
(curl_res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code)) != CURLE_OK)
{
fclose(f);
curl_easy_cleanup(curl);
if (curl_res)
{
snprintf(g->Message, sizeof(g->Message),
"curl returned this error code: %u "
"with the following error message: %s", curl_res,
curl_errbuf[0] ? curl_errbuf : curl_easy_strerror(curl_res));
return 1;
}
}
fclose(f);
curl_easy_cleanup(curl);| if (is_error) | ||
| { | ||
| snprintf(g->Message, sizeof(g->Message), "server error"); | ||
| return 1; | ||
| } |
There was a problem hiding this comment.
| { | ||
| CURL *curl = curl_easy_init(); | ||
| CURLcode curl_res = CURLE_OK; | ||
| char buf[512]; |
| struct MemoryStruct { | ||
| char *memory; | ||
| size_t size; | ||
| }; |
Rather than its fragile and gastly use of the curl executable.