Skip to content

Possible memory leak of logLevelStr in logging (never freed on any path) #119

Description

@OvOhao

Possible memory leak of logLevelStr in logging (never freed on any path)

I found a possible per-operation heap leak in the logging N-API method. When called with a
string argument (to set the libav log level), the function mallocs a buffer to hold a UTF-8
copy of the argument, but that buffer is never freed on any path — neither the error path
(unrecognised level throws and returns) nor the normal success path. Every call to
beamcoder.logging('<level>') leaks the allocation.

File: src/log.cc

Function: logging

    status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &strLen);
    CHECK_STATUS;
    logLevelStr = (char*) malloc(sizeof(char) * (strLen + 1));
    CHECK_STATUS;
    status = napi_get_value_string_utf8(env, args[0], logLevelStr, strLen + 1, &strLen);
    CHECK_STATUS;

    logLevel = beam_lookup_enum(beam_logging_level->inverse, logLevelStr);
    if (logLevel == BEAM_ENUM_UNKNOWN) {
      NAPI_THROW_ERROR("Logging level string unrecognised");   // returns, logLevelStr leaked
    }
    av_log_set_level(logLevel);

    status = napi_get_undefined(env, &result);
    CHECK_STATUS;
  }

  return result;   // success path also returns without free(logLevelStr)
  1. logLevelStr = (char*) malloc(strLen + 1) allocates a heap buffer holding the argument string.
  2. If the string is not a known level, NAPI_THROW_ERROR(...) expands to napi_throw_error(...); return nullptr; — it does not free logLevelStr, so the buffer leaks on the error path.
  3. On the normal path, after av_log_set_level(logLevel), the function falls through to
    return result;. There is no free(logLevelStr) anywhere in the function (confirmed: the only
    references to logLevelStr in log.cc are the declaration, the malloc, the fill, and the
    lookup). So the buffer leaks on the success path too.

This is a per-operation leak: it happens on every invocation with a string, not just a one-time
init, and the amount leaked scales with the argument length.

JS trigger (if applicable):

const beamcoder = require('beamcoder');
for (let i = 0; i < 1e6; i++) beamcoder.logging('debug'); // leaks a buffer each call
beamcoder.logging('not-a-level'); // also leaks (and throws)

Suggested fix: free(logLevelStr) before both the NAPI_THROW_ERROR return and the final
return result; (or use a small RAII/std::string holder for the copied string).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions