Skip to content

MDEV-30432 replace curl rest connector with libcurl usage#5071

Draft
grooverdan wants to merge 3 commits into
MariaDB:mainfrom
grooverdan:13.1-anel-connect-curl-MDEV-30432-v1-after-review
Draft

MDEV-30432 replace curl rest connector with libcurl usage#5071
grooverdan wants to merge 3 commits into
MariaDB:mainfrom
grooverdan:13.1-anel-connect-curl-MDEV-30432-v1-after-review

Conversation

@grooverdan
Copy link
Copy Markdown
Member

Rather than its fragile and gastly use of the curl executable.

an3l and others added 2 commits May 13, 2026 16:56
- Remove GetRest occurance
- Add libcurl feature to ConnectSE
- This patch closes MDEV-26727 (tested with Docker)
@grooverdan grooverdan added the MariaDB Foundation Pull requests created by MariaDB Foundation label May 13, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +201 to +228
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;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

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);

Comment on lines +247 to +255
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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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);

Comment on lines +257 to +281
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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Multiple issues with file handling:

  1. The return value of fopen is not checked. If it fails, f is NULL, which will cause a crash in fclose(f) at line 281 and potentially inside libcurl if CURLOPT_WRITEDATA is used with a NULL pointer and the default write function.
  2. If any curl_easy_setopt or curl_easy_perform call fails, the function returns without calling fclose(f), resulting in a file descriptor leak.
  3. If fopen fails, 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);

Comment on lines +283 to +287
if (is_error)
{
snprintf(g->Message, sizeof(g->Message), "server error");
return 1;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message "server error" is too generic. Including the HTTP response code would greatly improve debuggability when a REST request fails with a non-2xx status.

  if (is_error)
  {
    snprintf(g->Message, sizeof(g->Message), "Server returned error code %ld", http_code);
    return 1;
  }

{
CURL *curl = curl_easy_init();
CURLcode curl_res = CURLE_OK;
char buf[512];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

A 512-byte buffer for the URL is likely insufficient for many REST API calls, which can have long paths and numerous query parameters. Consider increasing this limit or using dynamic allocation.

Comment thread storage/connect/tabrest.h
Comment on lines +24 to +27
struct MemoryStruct {
char *memory;
size_t size;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The MemoryStruct structure is defined but not used in the implementation. It should be removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

MariaDB Foundation Pull requests created by MariaDB Foundation

Development

Successfully merging this pull request may close these issues.

2 participants