Skip to content

Conversation

@rajamah
Copy link
Member

@rajamah rajamah commented Jan 6, 2025

Issue:
AwtWindow::SetIconData leaks the old icon handles in hOldIcon and hOldIconSm if CreateIconFromRaster raises an exception. Additionally, an exception is checked only after the first call to CreateIconFromRaster.

Solution:
I have added the exception handling code to take care that the handles are properly destroyed and not leaked.

Testing:
I have tested the code to make sure there are no regressions caused by this.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8282862: AwtWindow::SetIconData leaks old icon handles if an exception is detected (Bug - P3)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/22932/head:pull/22932
$ git checkout pull/22932

Update a local copy of the PR:
$ git checkout pull/22932
$ git pull https://git.openjdk.org/jdk.git pull/22932/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 22932

View PR using the GUI difftool:
$ git pr show -t 22932

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/22932.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Jan 6, 2025

👋 Welcome back rmahajan! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Jan 6, 2025

@rajamah This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8282862: AwtWindow::SetIconData leaks old icon handles if an exception is detected

Reviewed-by: aivanov, dmarkov, prr, honkar, azvegint

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 298 new commits pushed to the master branch:

  • 356e2a8: 8348406: Remove tests GrantAllPermToExtWhenNoPolicy and PrincipalExpansionError from problem list
  • 3ebf889: 8348327: Incorrect march flag when building libsleef/vector_math_neon.c
  • d9d2e19: 8348365: Bad format string in CLDRDisplayNamesTest
  • 59e7509: 8348301: Remove unused Reference.waitForReferenceProcessing break-ins in tests
  • 605b53e: 8348299: Update List/ItemEventTest/ItemEventTest.java
  • cba0f78: 8348387: Add fixpath if needed for user-supplied tools
  • 44e5cca: 8348391: Keep case if possible for TOPDIR
  • 7460a0a: 8348392: Make claims "other matches are possible" even when that is not true
  • 5cc690d: 8347994: Add additional diagnostics to macOS failure handler to assist with diagnosing MCast test failures
  • c00557f: 8345049: Remove the jmx.tabular.data.hash.map compatibility property
  • ... and 288 more: https://git.openjdk.org/jdk/compare/a77ed30fcc3360cd16a11b1899f52f7e871df1df...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@honkar-jdk, @aivanov-jdk, @azvegint, @dmarkov20, @prrace) but any other Committer may sponsor as well.

➡️ To flag this PR as ready for integration with the above commit message, type /integrate in a new comment. (Afterwards, your sponsor types /sponsor in a new comment to perform the integration).

@openjdk openjdk bot added the rfr Pull request is ready for review label Jan 6, 2025
@openjdk
Copy link

openjdk bot commented Jan 6, 2025

@rajamah The following label will be automatically applied to this pull request:

  • client

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added the client client-libs-dev@openjdk.org label Jan 6, 2025
@mlbridge
Copy link

mlbridge bot commented Jan 6, 2025

Webrevs

Copy link
Contributor

@honkar-jdk honkar-jdk left a comment

Choose a reason for hiding this comment

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

Please update the copyright year for .cpp file.
Is there a way to test this fix or possibly add a test case?

Comment on lines 2125 to 2130
try {
m_hIcon = CreateIconFromRaster(env, iconRaster, w, h);
JNU_CHECK_EXCEPTION(env); // Might throw here
m_hIconSm = CreateIconFromRaster(env, smallIconRaster, smw, smh);
JNU_CHECK_EXCEPTION(env); // Or here
} catch (...) {
Copy link
Member

Choose a reason for hiding this comment

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

At first I thought using C++ exception handling was a bad idea, but it's actually needed. The code in CreateIconFromRaster can throw a C++ exception, specifically BitmapUtil::CreateTransparencyMaskFromARGB and BitmapUtil::CreateV4BitmapFromARGB.

try {
iconRasterBuffer = (int *)env->GetPrimitiveArrayCritical(iconRaster, 0);
JNI_CHECK_NULL_GOTO(iconRasterBuffer, "iconRaster data", done);
mask = BitmapUtil::CreateTransparencyMaskFromARGB(w, h, iconRasterBuffer);
image = BitmapUtil::CreateV4BitmapFromARGB(w, h, iconRasterBuffer);
} catch (...) {
if (iconRasterBuffer != NULL) {
env->ReleasePrimitiveArrayCritical(iconRaster, iconRasterBuffer, 0);
}
throw;
}

Since the exception is re-thrown in the handler there, it has to be caught in AwtWindow::SetIconData. Yet you shouldn't re-throw the C++ exception here, it must not escape JNI code, otherwise, the unhandled exception will likely crash the Java process.


try {
m_hIcon = CreateIconFromRaster(env, iconRaster, w, h);
JNU_CHECK_EXCEPTION(env); // Might throw here
Copy link
Member

Choose a reason for hiding this comment

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

The comment is misleading, JNU_CHECK_EXCEPTION does not throw an exception.

#define JNU_CHECK_EXCEPTION(env) \
do { \
if ((env)->ExceptionCheck()) { \
return; \
} \
} while (0) \

It checks if an exception is raised in Java code and returns immediately if there's a pending Java exception.

If a Java exception is raised at this line, after m_hIcon is assigned and its value is not NULL, it has to be deleted.

Perhaps, you have to handle it explicitly here:

        if (env->ExceptionCheck()) {
            if (m_hIcon) {
                DestroyIcon(m_hIcon);
            }
            return;
        }

It looks that the original values stored in hOldIcon and hOldIconSm should be restored if an error occurs.

m_hIcon = CreateIconFromRaster(env, iconRaster, w, h);
JNU_CHECK_EXCEPTION(env); // Might throw here
m_hIconSm = CreateIconFromRaster(env, smallIconRaster, smw, smh);
JNU_CHECK_EXCEPTION(env); // Or here
Copy link
Member

Choose a reason for hiding this comment

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

The clean-up code here should delete both m_hIcon and m_hIconSm.

And restore the previous values?

if (m_hIconSm != NULL) {
DestroyIcon(m_hIconSm);
}
throw; // Re-throw the exception
Copy link
Member

Choose a reason for hiding this comment

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

The C++ exception shouldn't be re-thrown here.

Whether we should raise a Java exception to indicate an error is to be discussed. If we restore the state of the AwtWindow object as if no failure occurred, it can continue. And I don't see any meaningful way to return an error back to Java here.

CreateIconFromRaster throws NullPointerException if it fails to get the image raster.

iconRasterBuffer = (int *)env->GetPrimitiveArrayCritical(iconRaster, 0);
JNI_CHECK_NULL_GOTO(iconRasterBuffer, "iconRaster data", done);

where JNI_CHECK_NULL_GOTO is

#define JNI_CHECK_NULL_GOTO(obj, msg, where) { \
if (obj == NULL) { \
env->ExceptionClear(); \
JNU_ThrowNullPointerException(env, msg); \
goto where; \
} \
}

@aivanov-jdk
Copy link
Member

The suggested fix does not address the reported issue yet: both hOldIcon and hOldIconSm are still leaked if the function exists prematurely.

Based on my above comments, I suggest restructuring the code.

  1. Create new icons using CreateIconFromRaster. Do not assign the handles to member fields m_hIcon and m_hIconSm just yet.
  2. Wrap the calls to CreateIconFromRaster in step 1 into a try-catch block to prevent a C++ exception escaping from JNI code as well as ensure a Java exception isn't raised.
    1. If any exception occurs, destroy the newly created icon handles ((new_)hIcon and (new_)hIconSm) and exit.
      In this case both m_hIcon and m_hIconSm remain unmodified—the current icons remain unchanged.
    2. If no exception occurs,
      1. Assign the old values of m_hIcon and m_hIconSm to temporary variables (hOldIcon and hOldIconSm),
        or call DestroyIcon right on the member fields m_*;
      2. Assign the newly created icons to the member fields m_hIcon and m_hIconSm and execute the code below to update the inherited icons;
      3. Call DestroyIcon on hOldIcon and hOldIconSm.

Copy link
Member

@aivanov-jdk aivanov-jdk left a comment

Choose a reason for hiding this comment

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

Looks good to me except for a tiny formatting nit.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Jan 16, 2025
@aivanov-jdk
Copy link
Member

Is there a way to test this fix or possibly add a test case?

@honkar-jdk I don't think it's possible to write a test for this case….

It my be tested by defining OUTOFMEM_TEST in awt_new.cpp to enable random allocation failures.

Another way could be to change the source code to explicitly cause std::bad_alloc and/or a Java exception in any of the called methods. Just to validate the suggested fix really works… on top of static code analysis.

@honkar-jdk
Copy link
Contributor

honkar-jdk commented Jan 17, 2025

It my be tested by defining OUTOFMEM_TEST in awt_new.cpp to enable random allocation failures.

Another way could be to change the source code to explicitly cause std::bad_alloc and/or a Java exception in any of the called methods. Just to validate the suggested fix really works… on top of static code analysis.

@aivanov-jdk Thanks for clarifying.

@openjdk openjdk bot added ready Pull request is ready to be integrated and removed ready Pull request is ready to be integrated labels Jan 17, 2025
@openjdk openjdk bot removed the ready Pull request is ready to be integrated label Jan 17, 2025
@openjdk openjdk bot added the ready Pull request is ready to be integrated label Jan 17, 2025
Copy link
Contributor

@prrace prrace left a comment

Choose a reason for hiding this comment

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

Although this leak is extremely unlikely, leaks of GDI resources can bring even a modern windows system to its knees in a matter of seconds ..

{
HICON hNewIcon = NULL;
HICON hNewIconSm = NULL;

Copy link
Contributor

Choose a reason for hiding this comment

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

I presume we know for sure there's no exception pending when we enter ?
Looking at the only caller, it seems probable.

Copy link
Member Author

@rajamah rajamah Jan 17, 2025

Choose a reason for hiding this comment

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

Are you suggesting we add a JNU_CHECK_EXCEPTION the beginning of the function? , as I don't think we know for sure there is an exception pending or not at this point.

Copy link
Member

Choose a reason for hiding this comment

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

I presume we know for sure there's no exception pending when we enter ?
Looking at the only caller, it seems probable.

There can't be a pending exception when AwtWindow::SetIconData starts.

The chain starts with JNI method Java_sun_awt_windows_WWindowPeer_setIconImagesData which calls AwtWindow::_SetIconImagesData wrapped in SyncCall.

No Java code is called before execution gets into SetIconData where CreateIconFromRaster is called.

…I don't think we know for sure there is an exception pending or not at this point.

I'm sure there's no pending exception on the entry to AwtWindow::SetIconData.

HICON hOldIcon = NULL;
HICON hOldIconSm = NULL;
//Destroy previous icon if it isn't inherited
if ((m_hIcon != NULL) && !m_iconInherited) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure why the check for NULL is needed .. but it is OK.

Copy link
Member

Choose a reason for hiding this comment

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

What do you mean? That it could've been just if ((m_hIcon) && !m_iconInherited)?

This line is unchanged.
In the majority of cases the surrounding code uses explicit != NULL in if-statements.

@aivanov-jdk
Copy link
Member

While I was looking deeper into the code flow, I found another place which needs changing.

JNIEXPORT void JNICALL Java_sun_awt_windows_WTaskbarPeer_setOverlayIcon
(JNIEnv *env, jobject, jlong window, jintArray buf, jint w, jint h)
{
HICON icon = CreateIconFromRaster(env, buf, w, h);
m_Taskbar->SetOverlayIcon((HWND)window, icon, NULL);
::DestroyIcon(icon);
}

The JNI method Java_sun_awt_windows_WTaskbarPeer_setOverlayIcon for WTaskbarPeer.setOverlayIcon calls CreateIconFromRaster that can throw a C++ exception.

The call to CreateIconFromRaster has to be wrapped into a try-catch block to ensure the C++ exception does not escape.

I think it's reasonable to address this new issue under a new bugid.

@aivanov-jdk
Copy link
Member

The call to CreateIconFromRaster has to be wrapped into a try-catch block to ensure the C++ exception does not escape.

I think it's reasonable to address this new issue under a new bugid.

I submitted JDK-8282862: Catch C++ exception in Java_sun_awt_windows_WTaskbarPeer_setOverlayIcon.

@rajamah
Copy link
Member Author

rajamah commented Jan 23, 2025

/integrate

@openjdk openjdk bot added the sponsor Pull request is ready to be sponsored label Jan 23, 2025
@openjdk
Copy link

openjdk bot commented Jan 23, 2025

@rajamah
Your change (at version 2f47a55) is now ready to be sponsored by a Committer.

@aivanov-jdk
Copy link
Member

/sponsor

@openjdk
Copy link

openjdk bot commented Jan 23, 2025

Going to push as commit 48ece07.
Since your change was applied there have been 298 commits pushed to the master branch:

  • 356e2a8: 8348406: Remove tests GrantAllPermToExtWhenNoPolicy and PrincipalExpansionError from problem list
  • 3ebf889: 8348327: Incorrect march flag when building libsleef/vector_math_neon.c
  • d9d2e19: 8348365: Bad format string in CLDRDisplayNamesTest
  • 59e7509: 8348301: Remove unused Reference.waitForReferenceProcessing break-ins in tests
  • 605b53e: 8348299: Update List/ItemEventTest/ItemEventTest.java
  • cba0f78: 8348387: Add fixpath if needed for user-supplied tools
  • 44e5cca: 8348391: Keep case if possible for TOPDIR
  • 7460a0a: 8348392: Make claims "other matches are possible" even when that is not true
  • 5cc690d: 8347994: Add additional diagnostics to macOS failure handler to assist with diagnosing MCast test failures
  • c00557f: 8345049: Remove the jmx.tabular.data.hash.map compatibility property
  • ... and 288 more: https://git.openjdk.org/jdk/compare/a77ed30fcc3360cd16a11b1899f52f7e871df1df...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Jan 23, 2025
@openjdk openjdk bot closed this Jan 23, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review sponsor Pull request is ready to be sponsored labels Jan 23, 2025
@openjdk
Copy link

openjdk bot commented Jan 23, 2025

@aivanov-jdk @rajamah Pushed as commit 48ece07.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client client-libs-dev@openjdk.org integrated Pull request has been integrated

Development

Successfully merging this pull request may close these issues.

6 participants