Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HPCC-33299: Fix ZAP file log filter leaks #19466

Open
wants to merge 2 commits into
base: candidate-9.10.x
Choose a base branch
from

Conversation

timothyklemm
Copy link
Contributor

@timothyklemm timothyklemm commented Jan 30, 2025

  • Own filter objects prior to throwing exceptions.
  • Ensure new filter object references are owned before they are linked in new compound filter objects, or released after they are linked.

Type of change:

  • This change is a bug fix (non-breaking change which fixes an issue).
  • This change is a new feature (non-breaking change which adds functionality).
  • This change improves the code (refactor or other change that does not change the functionality)
  • This change fixes warnings (the fix does not alter the functionality or the generated code)
  • This change is a breaking change (fix or feature that will cause existing behavior to change).
  • This change alters the query API (existing queries will have to be recompiled)

Checklist:

  • My code follows the code style of this project.
    • My code does not create any new warnings from compiler, build system, or lint.
  • The commit message is properly formatted and free of typos.
    • The commit message title makes sense in a changelog, by itself.
    • The commit is signed.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly, or...
    • I have created a JIRA ticket to update the documentation.
    • Any new interfaces or exported functions are appropriately commented.
  • I have read the CONTRIBUTORS document.
  • The change has been fully tested:
    • I have added tests to cover my changes.
    • All new and existing tests passed.
    • I have checked that this change does not introduce memory leaks.
    • I have used Valgrind or similar tools to check for potential issues.
  • I have given due consideration to all of the following potential concerns:
    • Scalability
    • Performance
    • Security
    • Thread-safety
    • Cloud-compatibility
    • Premature optimization
    • Existing deployed queries will not be broken
    • This change fixes the problem, not just the symptom
    • The target branch of this pull request is appropriate for such a change.
  • There are no similar instances of the same problem that should be addressed
    • I have addressed them here
    • I have raised JIRA issues to address them separately
  • This is a user interface / front-end modification
    • I have tested my changes in multiple modern browsers
    • The component(s) render as expected

Smoketest:

  • Send notifications about my Pull Request position in Smoketest queue.
  • Test my draft Pull Request.

Testing:

Copy link

Jira Issue: https://hpccsystems.atlassian.net//browse/HPCC-33299

Jirabot Action Result:
Workflow Transition To: Merge Pending
Updated PR

@@ -4270,7 +4270,8 @@ void CWsWuFileHelper::readWULogToFiles(IConstWorkUnit *cwu, WsWuInfo &winfo, con
ForEach(*iter)
{
const char *processName = iter->query().queryProp("@podName");
ILogAccessFilter *processLogFetchFilter = getBinaryLogAccessFilter(logFetchFilter, getPodLogAccessFilter(processName), LOGACCESS_FILTER_and);
Owned<ILogAccessFilter> podFilter = getPodLogAccessFilter(processName);
ILogAccessFilter *processLogFetchFilter = getBinaryLogAccessFilter(logFetchFilter, podFilter, LOGACCESS_FILTER_and);
Copy link
Member

Choose a reason for hiding this comment

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

Per our offline conversation, let's be explicit about pointer ownership for the sake of maintainability, this is a low throughput feature and we're ok accepting the added object creation overhead.

@@ -260,7 +260,7 @@ struct WUComponentLogOptions
}

if (componentsFilterObj != nullptr)
logFetchFilter = getBinaryLogAccessFilter(logFetchFilter, componentsFilterObj, LOGACCESS_FILTER_and);
logFetchFilter.setown(getBinaryLogAccessFilterOwn(logFetchFilter.getLink(), componentsFilterObj, LOGACCESS_FILTER_and));
Copy link
Member

Choose a reason for hiding this comment

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

per offline convo: let's try to externalize the repeating logic into a function.

* Ownership of the given reference is transferred to the function.
* @param operation The logical operation to be performed on the existing and new filters.
*/
void extendFetchFilter(Owned<ILogAccessFilter>& compoundFilter, ILogAccessFilter* extension, LogAccessFilterType operation)
Copy link
Member

Choose a reason for hiding this comment

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

we should expose this logic at the jlog.hpp level as compoundOwnedFilter(ownedfilter, extention, op)

Copy link
Member

@rpastrana rpastrana left a comment

Choose a reason for hiding this comment

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

Copy link
Member

@rpastrana rpastrana left a comment

Choose a reason for hiding this comment

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

- Own filter objects prior to throwing exceptions.
- Ensure new filter object references are owned before they are linked in
  new compound filter objects, or released after they are linked.

Signed-off-by: Tim Klemm <[email protected]>
@timothyklemm timothyklemm force-pushed the hpcc-33299-zap-filter-leaks branch from c03c4a8 to 6892834 Compare February 10, 2025 16:37
@timothyklemm
Copy link
Contributor Author

@ghalliday please merge.

Copy link
Member

@jakesmith jakesmith left a comment

Choose a reason for hiding this comment

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

@timothyklemm - please see comment re. ownership semantics.

Also, minor, but it looks like buildBinaryLogFilter in WsLogAccessService.cpp could also leak under the right circumstances, e.g. if leftFilter is created, but right is not and an exception is thrown. Would be good if the filters were Owned, then 'getBinaryLogAccessFilter' was called at the end (not getBinaryLogAccessFilterOwn'

ILogAccessFilter *processLogFetchFilter = getBinaryLogAccessFilter(logFetchFilter, getPodLogAccessFilter(processName), LOGACCESS_FILTER_and);
zapLogFilterOptions.logFilter.logFetchOptions.setFilter(processLogFetchFilter);
Owned<ILogAccessFilter> processLogFetchFilter(logFetchFilter.getLink()); // retain original for next iteration
compoundOwnedFilter(processLogFetchFilter, getPodLogAccessFilter(processName), LOGACCESS_FILTER_and);
Copy link
Member

Choose a reason for hiding this comment

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

there's some unusual ownership semantics going on here. It's generally not good practice to pass in Owned objects and fill them in methods.

If I've read correctly, I'd expect this code to be something like:

        Owned<ILogAccessFilter> podFilter = getPodLogAccessFilter(processName);
        Owned<ILogAccessFilter> compoundFilter = getCompoundFilter(logFetchFilter, podFilter, LOGACCESS_FILTER_and);
        zapLogFilterOptions.logFilter.logFetchOptions.setFilter(compoundFilter.getClear());

i.e. where the helper takes both filters, and returns a new one.

where getCompoundFilter is something like:

ILogAccessFilter *getCompoundFilter(ILogAccessFilter* arg1, ILogAccessFilter* arg2, LogAccessFilterType operation)
{
    if (!arg1)
        return LINK(arg2);
    else if (!arg2)
        return LINK(arg1);
    else
        return getBinaryLogAccessFilter(arg1, arg2, operation);
}

Copy link
Member

@jakesmith jakesmith left a comment

Choose a reason for hiding this comment

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

@timothyklemm - looks good to me. 1 trivial comment.

@@ -326,15 +333,24 @@ struct WUComponentLogOptions
logFetchOptions.setStartFrom(logFilterReq.getLineStartFrom());

if (logFilterReq.getComponentsFilter().length() > 0)
compoundOwnedFilter(logFetchFilter, getOredComponentsLogFilter(logFilterReq.getComponentsFilter()), LOGACCESS_FILTER_and);
{
Owned<ILogAccessFilter> filterClause = getOredComponentsLogFilter(logFilterReq.getComponentsFilter());
Copy link
Member

Choose a reason for hiding this comment

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

trivial: there's a mix of styles here assigning to Owned, here by assignment,
in other places via the ctor (e.g. line 274). Either fine, no real benefit of either, assignment is more typical.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants