librz/bin/pdb: fix memory leaks of PDB callable convention strings and orphaned callables (#6468)

Leak A - calling-convention string leaked
Leak B - orphaned callables on duplicate names

Co-authored-by: Anton Kochkov <anton.kochkov@gmail.com>
This commit is contained in:
NOT XVilka 2026-06-09 12:23:56 +08:00 committed by GitHub
parent fb99604963
commit 940292aaae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 38 additions and 11 deletions

View file

@ -185,7 +185,21 @@ static RzType *procedure_parse(
if (arglist) {
arglist_parse(typedb, stream, arglist, typ->callable->args);
}
rz_type_func_save((RzTypeDB *)typedb, callable);
if (!rz_type_func_save((RzTypeDB *)typedb, callable)) {
// A callable with this name is already registered. PDB type graphs
// frequently reference the same procedure type more than once, and
// unnamed procedures get a deterministic name derived from their TPI
// offset, so duplicates are expected. rz_type_func_save() does not take
// ownership on failure, so reuse the already-stored callable and free
// this one; otherwise it (and its whole subtree) would be leaked.
RzCallable *existing = rz_type_func_get((RzTypeDB *)typedb, callable->name);
rz_type_callable_free(callable);
typ->callable = existing;
if (!existing) {
rz_type_free(typ);
return NULL;
}
}
return typ;
}
@ -221,7 +235,18 @@ static RzType *mfunction_parse(const RzTypeDB *typedb, RzPdbTpiStream *stream, R
if (arglist) {
arglist_parse(typedb, stream, arglist, type->callable->args);
}
rz_type_func_save((RzTypeDB *)typedb, callable);
if (!rz_type_func_save((RzTypeDB *)typedb, callable)) {
// See procedure_parse(): rz_type_func_save() does not take ownership when
// a callable of the same name already exists, so free this duplicate and
// reuse the stored one to avoid leaking it together with its subtree.
RzCallable *existing = rz_type_func_get((RzTypeDB *)typedb, callable->name);
rz_type_callable_free(callable);
type->callable = existing;
if (!existing) {
rz_type_free(type);
return NULL;
}
}
return type;
}

View file

@ -34,27 +34,29 @@ int tpi_type_node_cmp(const void *incoming, const RBNode *in_tree, void *user) {
*
* \param idx
*/
RZ_API RZ_OWN char *rz_bin_pdb_calling_convention_as_string(RZ_NONNULL RzPdbTpiCallingConvention idx) {
RZ_API RZ_BORROW const char *rz_bin_pdb_calling_convention_as_string(RZ_NONNULL RzPdbTpiCallingConvention idx) {
// The returned string is a static literal and must not be freed by the
// caller. RzCallable.cc (where these are stored) is a borrowed pointer.
switch (idx) {
case NEAR_C:
case FAR_C:
return rz_str_dup("__cdecl");
return "__cdecl";
case NEAR_PASCAL:
case FAR_PASCAL:
return rz_str_dup("__pascal");
return "__pascal";
case NEAR_FAST:
case FAR_FAST:
return rz_str_dup("__fastcall");
return "__fastcall";
case NEAR_STD:
case FAR_STD:
return rz_str_dup("__stdcall");
return "__stdcall";
case NEAR_SYS:
case FAR_SYS:
return rz_str_dup("__syscall");
return "__syscall";
case THISCALL:
return rz_str_dup("__thiscall");
return "__thiscall";
case NEAR_VEC:
return rz_str_dup("__vectorcall");
return "__vectorcall";
default:
return NULL;
}

View file

@ -376,7 +376,7 @@ RZ_API bool rz_pdb_all_symbols_foreach(
// TPI
RZ_API RZ_BORROW RzPdbTpiType *rz_bin_pdb_get_type_by_index(RZ_NONNULL RzPdbTpiStream *stream, ut32 index);
RZ_API RZ_OWN char *rz_bin_pdb_calling_convention_as_string(RZ_NONNULL RzPdbTpiCallingConvention idx);
RZ_API RZ_BORROW const char *rz_bin_pdb_calling_convention_as_string(RZ_NONNULL RzPdbTpiCallingConvention idx);
RZ_API bool rz_bin_pdb_type_is_fwdref(RZ_NONNULL RzPdbTpiType *t);
RZ_API RZ_BORROW RzPVector /*<RzPdbTpiType *>*/ *rz_bin_pdb_get_type_members(RZ_NONNULL RzPdbTpiStream *stream, RzPdbTpiType *t);
RZ_API RZ_BORROW char *rz_bin_pdb_get_type_name(RZ_NONNULL RzPdbTpiType *type);