Possible memory leak and new/free mismatch when releasing the cups_dest_t from getPrinter
I found a possible per-operation heap leak (and a new/free mismatch) in the printer-info
callbacks. getPrinter returns a deep copy of a cups_dest_t: the struct itself is allocated with
new, and its name/instance are strdup'd while its options array is allocated with
new[] and each option's name/value is strdup'd. Every N-API callback that consumes this
copy releases it with a bare free(dest), which (1) only frees the top-level struct — leaking the
strdup'd name, instance, the new[] options array, and all 2 * num_options strdup'd
option strings — and (2) frees memory obtained from new with free, which is undefined behaviour.
File: lib/unix/methods.cpp (allocation) and lib/unix/API.cpp (release)
Function: methods::getPrinter / methods::copyDest (alloc) and API::printerInfo /
API::printerOptions / API::defaultPrinterName / API::print (release)
// methods.cpp — getPrinter builds a deep copy
cups_dest_t* result = new cups_dest_t; // struct via new
...
// copyDest fills it:
dest->instance = strdup(source->instance); // strdup
dest->name = strdup(source->name); // strdup
dest->options = new cups_option_t[source->num_options]; // new[]
// copyOptions: dest[i].name = strdup(...); dest[i].value = strdup(...);
// API.cpp — every consumer releases with a bare free()
cups_dest_t* dest = getPrinter(*printer);
...
cupsFreeJobs(num_jobs, printerJobs);
free(dest); // frees only the struct; UB (new vs free)
getPrinter (methods.cpp:25) allocates the destination struct with new cups_dest_t and calls
copyDest, which strdups instance and name, allocates dest->options with
new cups_option_t[num_options], and copyOptions strdups each option name and value.
- Each callback in API.cpp (
printerInfo line 80, printerOptions line 142,
defaultPrinterName line 155, print line 179) releases the returned pointer with a single
free(dest).
free(dest) releases only the top-level block. It never frees dest->name, dest->instance,
the dest->options array, or the num_options pairs of strdup'd strings — so all of those
leak on every call. Because these callbacks are the normal per-request API surface, the leak
accumulates with each printer query.
- Additionally,
dest was created with new (not malloc), so passing it to free is a
new/free mismatch (CWE-762) — undefined behaviour on top of the leak.
JS trigger (if applicable):
const printer = require('node-native-printer');
for (let i = 0; i < 100000; i++) printer.printerInfo('SomePrinter'); // leaks strings + options each call
Suggested fix: give the deep copy a matching destructor/free helper that mirrors copyDest —
free/delete[] the strdup'd name/instance, delete[] each option's strdup'd
name/value and the options array, then delete dest (matching new). Replace every
free(dest) call with that helper. (CUPS also offers no public free for a hand-rolled copy, so a
dedicated helper is required.)
Possible memory leak and new/free mismatch when releasing the
cups_dest_tfromgetPrinterI found a possible per-operation heap leak (and a
new/freemismatch) in the printer-infocallbacks.
getPrinterreturns a deep copy of acups_dest_t: the struct itself is allocated withnew, and itsname/instancearestrdup'd while itsoptionsarray is allocated withnew[]and each option'sname/valueisstrdup'd. Every N-API callback that consumes thiscopy releases it with a bare
free(dest), which (1) only frees the top-level struct — leaking thestrdup'dname,instance, thenew[] optionsarray, and all2 * num_optionsstrdup'doption strings — and (2) frees memory obtained from
newwithfree, which is undefined behaviour.File:
lib/unix/methods.cpp(allocation) andlib/unix/API.cpp(release)Function:
methods::getPrinter/methods::copyDest(alloc) andAPI::printerInfo/API::printerOptions/API::defaultPrinterName/API::print(release)getPrinter(methods.cpp:25) allocates the destination struct withnew cups_dest_tand callscopyDest, whichstrdupsinstanceandname, allocatesdest->optionswithnew cups_option_t[num_options], andcopyOptionsstrdups each optionnameandvalue.printerInfoline 80,printerOptionsline 142,defaultPrinterNameline 155,printline 179) releases the returned pointer with a singlefree(dest).free(dest)releases only the top-level block. It never freesdest->name,dest->instance,the
dest->optionsarray, or thenum_optionspairs ofstrdup'd strings — so all of thoseleak on every call. Because these callbacks are the normal per-request API surface, the leak
accumulates with each printer query.
destwas created withnew(notmalloc), so passing it tofreeis anew/freemismatch (CWE-762) — undefined behaviour on top of the leak.JS trigger (if applicable):
Suggested fix: give the deep copy a matching destructor/free helper that mirrors
copyDest—free/delete[]thestrdup'dname/instance,delete[]each option'sstrdup'dname/valueand theoptionsarray, thendelete dest(matchingnew). Replace everyfree(dest)call with that helper. (CUPS also offers no public free for a hand-rolled copy, so adedicated helper is required.)