urlapi: fix memleaks on error in parse_hostname_login()

Detected by GitHub Code Quality

Follow-up to acd82c8bfd #11006
Follow-up to 4183b8fe9a #8049

Closes #21879
This commit is contained in:
Viktor Szakats 2026-06-05 16:39:20 +02:00
parent 9c1ebea359
commit 7c34365cce
No known key found for this signature in database
2 changed files with 75 additions and 10 deletions

View file

@ -253,12 +253,18 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace)
* Parse the login details (username, password and options) from the URL and
* strip them out of the hostname
*
* @unittest 1675
*/
static CURLUcode parse_hostname_login(struct Curl_URL *u,
const char *login,
size_t len,
unsigned int flags,
size_t *offset) /* to the hostname */
UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u,
const char *login,
size_t len,
unsigned int flags,
size_t *hostname_offset);
UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u,
const char *login,
size_t len,
unsigned int flags,
size_t *hostname_offset)
{
CURLUcode ures = CURLUE_OK;
CURLcode result;
@ -278,7 +284,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
DEBUGASSERT(login);
*offset = 0;
*hostname_offset = 0;
ptr = memchr(login, '@', len);
if(!ptr)
goto out;
@ -326,7 +332,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
}
/* the hostname starts at this offset */
*offset = ptr - login;
*hostname_offset = ptr - login;
return CURLUE_OK;
out:
@ -334,9 +340,9 @@ out:
curlx_free(userp);
curlx_free(passwdp);
curlx_free(optionsp);
u->user = NULL;
u->password = NULL;
u->options = NULL;
curlx_safefree(u->user);
curlx_safefree(u->password);
curlx_safefree(u->options);
return ures;
}

View file

@ -383,5 +383,64 @@ loop_end:
}
#endif /* !CURL_DISABLE_HTTP */
/* Test parse_hostname_login */
{
struct Curl_URL u;
int fails = 0;
unsigned int i;
struct test {
CURLUcode uc;
const char *in;
const char *scheme;
unsigned int flags;
const char *user;
const char *password;
const char *options;
size_t offset;
};
const struct test tests[] = {
{ CURLUE_OK, "foo:bar@host", NULL, 0, "foo", "bar", "o", 8 },
{ CURLUE_OK, "foo:bar;abc@host", "imap", 0, "foo", "bar", "abc", 12 },
{ CURLUE_OK, "foo:bar;abc@host", NULL, 0, "foo", "bar;abc", "o", 12 },
{ CURLUE_USER_NOT_ALLOWED, "foo:bar@host", NULL, CURLU_DISALLOW_USER,
NULL, NULL, NULL, 0 },
{ CURLUE_OK, "host", NULL, 0, NULL, NULL, NULL, 0 },
};
for(i = 0; i < CURL_ARRAYSIZE(tests); i++) {
CURLUcode uc;
size_t offset = 0;
memset(&u, 0, sizeof(u));
u.scheme = CURL_UNCONST(tests[i].scheme);
u.user = curlx_strdup("u");
u.password = curlx_strdup("p");
u.options = curlx_strdup("o");
uc = parse_hostname_login(&u, tests[i].in, strlen(tests[i].in),
tests[i].flags, &offset);
if(uc != tests[i].uc ||
!!u.user != !!tests[i].user ||
(u.user && tests[i].user &&
strcmp(u.user, tests[i].user)) ||
!!u.password != !!tests[i].password ||
(u.password && tests[i].password &&
strcmp(u.password, tests[i].password)) ||
!!u.options != !!tests[i].options ||
(u.options && tests[i].options &&
strcmp(u.options, tests[i].options)) ||
offset != tests[i].offset) {
curl_mfprintf(stderr, "%d: parse_hostname_login('%s') host failed:"
" expected '%d/%s/%s/%s/%zu', got '%d/%s/%s/%s/%zu'\n",
i, tests[i].in, (int)tests[i].uc, tests[i].user,
tests[i].password, tests[i].options, tests[i].offset,
(int)uc, u.user, u.password, u.options, offset);
fails++;
}
curlx_safefree(u.user);
curlx_safefree(u.password);
curlx_safefree(u.options);
}
abort_if(fails, "parse_hostname_login tests failed");
}
UNITTEST_END_SIMPLE
}