Skip to content

Remove stderr logging when libgssapi_krb5 is not available; redirect to NetEventSource.Error#126300

Open
Copilot wants to merge 5 commits intomainfrom
copilot/fix-runtime-logs-stderr
Open

Remove stderr logging when libgssapi_krb5 is not available; redirect to NetEventSource.Error#126300
Copilot wants to merge 5 commits intomainfrom
copilot/fix-runtime-logs-stderr

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 30, 2026

When libgssapi_krb5.so.2 is absent (e.g. chiseled containers), the native shim wrote directly to stderr before returning a failure code, causing spurious error output even when the caller handled the absence gracefully.

Changes

pal_gssapi.c

  • Removed fprintf(stderr, …) from the dlopen failure path in ensure_gss_shim_initialized() — the library is optional, so a missing library is silently handled.
  • Kept fprintf(stderr, …) for the dlsym failure path (PER_FUNCTION_BLOCK macro) — if the library is found but a required symbol is missing, that is unexpected and warrants a diagnostic message (consistent with the OpenSSL shim pattern).

Interop.NetSecurityNative.IsNtlmInstalled.cs

  • Changed GssInitializer's static constructor to throw DllNotFoundException("libgssapi_krb5.so.2") instead of a bare InvalidOperationException when GSSAPI initialization fails. Initialize() remains a no-op hook, preserving the original structure while making the propagated exception type more descriptive.

NegotiateAuthenticationPal.Unix.cs

  • Added catch (TypeInitializationException tie) when (tie.InnerException is DllNotFoundException) to both Create(NegotiateAuthenticationClientOptions) and Create(NegotiateAuthenticationServerOptions). The guard makes the handling specific to the GSSAPI library-not-found case; tie.InnerException (the DllNotFoundException with the library name) is passed to NetEventSource.Error for a more descriptive message.
  • CheckHasSystemNetSecurityNative() similarly logs the inner exception of a TypeInitializationException when available, for more descriptive tracing output.
  • Added NetEventSource.Error logging in CheckHasSystemNetSecurityNative() when any of the caught availability-check exceptions fires.

📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

…ging in managed code

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/9153c03c-ec0e-4f65-b140-ed29f159d0f9

Co-authored-by: rzikm <32671551+rzikm@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix runtime error logs when libgssapi_krb5 is missing Remove stderr logging when libgssapi_krb5 is not available; redirect to NetEventSource.Error Mar 30, 2026
Copilot AI requested a review from rzikm March 30, 2026 10:04
// GSSAPI shim may not be available on some platforms (Linux Bionic)
return new UnsupportedNegotiateAuthenticationPal(clientOptions);
}
catch (TypeInitializationException tie)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Where does the TypeInitializationException that this is catching come from? Can this error handling be more specific?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The TypeInitializationException comes from GssInitializer.Initialize(), which is called by NetSecurityNative's static constructor. Previously GssInitializer's static constructor threw a bare InvalidOperationException which was then wrapped in TypeInitializationException.

Changed in b04fdcb: GssInitializer.Initialize() now throws DllNotFoundException("libgssapi_krb5.so.2") directly (no longer from the static constructor), which becomes the inner exception of the TypeInitializationException. The catch is now more specific with when (tie.InnerException is DllNotFoundException).

Copy link
Copy Markdown
Member

@rzikm rzikm left a comment

Choose a reason for hiding this comment

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

@copilot Please review code review comments,

can we have the message in NetEventSource.Error be more descriptive? Can we internally propagate the error by a different exeption than TypeInitializationException?

…on in GssInitializer, add when guard

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/9feee3d0-a8a2-4360-a981-593fe45e6f8b

Co-authored-by: rzikm <32671551+rzikm@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 30, 2026

@copilot Please review code review comments,

can we have the message in NetEventSource.Error be more descriptive? Can we internally propagate the error by a different exeption than TypeInitialization...

Done in b04fdcb:

  • GssInitializer.Initialize() now throws DllNotFoundException("libgssapi_krb5.so.2") instead of a bare InvalidOperationException, so NetEventSource.Error now logs the DllNotFoundException with the library name as its message.
  • The TypeInitializationException catches now use tie.InnerException (the actual DllNotFoundException) for the log call, giving more descriptive output.
  • Also restored the fprintf(stderr, ...) for dlsym failures (unexpected missing symbols in a found library), as discussed.

Copilot AI requested a review from rzikm March 30, 2026 15:33
Copilot AI requested a review from jkotas March 30, 2026 15:59
@jkotas jkotas marked this pull request as ready for review March 30, 2026 16:55
Copilot AI review requested due to automatic review settings March 30, 2026 16:55
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the Unix Negotiate/GSSAPI integration to avoid writing directly to stderr when the optional libgssapi_krb5.so.2 is missing, and instead rely on managed tracing + more descriptive exceptions to communicate the “not available” condition.

Changes:

  • Native: stop printing dlopen-failure diagnostics to stderr when libgssapi_krb5.so.2 is absent.
  • Common interop: change GSS initializer failure to throw DllNotFoundException("libgssapi_krb5.so.2").
  • System.Net.Security: add handling/logging for GSS “library not found” initialization failures and add tracing for native-availability checks.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/native/libs/System.Net.Security.Native/pal_gssapi.c Removes stderr logging on dlopen failure for optional GSS library.
src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs Changes GSS init failure to throw DllNotFoundException with library name.
src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs Adds exception handling/logging for GSS “not installed” cases and logs availability-check failures via NetEventSource.

Comment on lines +789 to +790
Exception logException = e is TypeInitializationException tie ? tie.InnerException ?? e : e;
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, logException);
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

When logging TypeInitializationException, this only unwraps one level. In the common case here (NetSecurityNative cctor failing due to GssInitializer cctor), InnerException is another TypeInitializationException, so the logged exception won’t include the underlying DllNotFoundException directly. Consider unwrapping TypeInitializationException recursively so tracing shows the real root cause (and to keep behavior consistent with the Create() handling).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be better to avoid depending on TypeInitializationException unwrapping instead.

Comment on lines +25 to 32
private const string GssApiLibraryName = "libgssapi_krb5.so.2";

static GssInitializer()
{
if (EnsureGssInitialized() != 0)
{
throw new InvalidOperationException();
throw new DllNotFoundException(GssApiLibraryName);
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

EnsureGssInitialized() returns -1 for both dlopen failures (libgssapi_krb5 missing) and dlsym failures (required symbol missing) in pal_gssapi.c. Throwing DllNotFoundException for any non-zero return conflates these cases and can cause callers to treat an unexpected symbol-missing scenario as a simple optional-library absence. Consider updating the native shim to return distinct error codes (e.g., library-not-found vs symbol-not-found) and throw a more accurate exception (e.g., EntryPointNotFoundException or InvalidOperationException) for the symbol-missing case.

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +63
catch (TypeInitializationException tie) when (tie.InnerException is DllNotFoundException)
{
// GSSAPI native library (e.g. libgssapi_krb5) may not be available on the system
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, tie.InnerException);
return new UnsupportedNegotiateAuthenticationPal(clientOptions);
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

The catch filter only matches when the thrown TypeInitializationException’s immediate InnerException is a DllNotFoundException. In this code path, Interop.NetSecurityNative’s type initializer calls GssInitializer.Initialize(), so failures typically become nested TypeInitializationExceptions (NetSecurityNative -> GssInitializer -> DllNotFoundException). As a result, this catch may not run and the exception can escape instead of falling back to Unsupported. Consider unwrapping TypeInitializationException(s) to the root cause (and logging/handling based on that).

Copilot uses AI. Check for mistakes.
Comment on lines +87 to +92
catch (TypeInitializationException tie) when (tie.InnerException is DllNotFoundException)
{
// GSSAPI native library (e.g. libgssapi_krb5) may not be available on the system
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, tie.InnerException);
return new UnsupportedNegotiateAuthenticationPal(serverOptions);
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Same issue as the client Create() overload: the filter only checks one InnerException level. Because NetSecurityNative’s static ctor invokes GssInitializer.Initialize(), missing libgssapi_krb5 typically results in nested TypeInitializationExceptions, so this catch may not trigger. Unwrap TypeInitializationException(s) to find the underlying DllNotFoundException and handle/log that.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #126300

Note

This review was generated by Copilot.

Holistic Assessment

Motivation: Justified. In chiseled/minimal containers without libgssapi_krb5.so.2, the native shim wrote spurious error output to stderr even when the managed caller handles the absence gracefully. Removing this noise and redirecting diagnostics to NetEventSource.Error is the right fix.

Approach: Good overall — remove noisy stderr at the source, use a more specific exception type (DllNotFoundException over InvalidOperationException), and add proper managed logging. The intentional retention of fprintf(stderr, ...) for the dlsym failure path (library loads but symbol missing) is a sound design choice.

Summary: ⚠️ Needs Human Review. The code is clean, follows established patterns, and is a clear improvement over the status quo. However, the when (tie.InnerException is DllNotFoundException) guard in the two Create methods may never match due to TypeInitializationException double-wrapping in the CLR (explained below). This warrants verification. I may be wrong about the nesting behavior, but the CLR source at src/coreclr/vm/methodtable.cpp:3723-3751 suggests double-wrapping does occur for nested type initializer failures.


Detailed Findings

⚠️ TypeInitializationException nesting — when guard may not match in Create methods

Files: NegotiateAuthenticationPal.Unix.cs lines 58, 87

The catch handler:

catch (TypeInitializationException tie) when (tie.InnerException is DllNotFoundException)

The exception propagation chain when libgssapi_krb5.so.2 is missing:

  1. GssInitializer cctor throws DllNotFoundException("libgssapi_krb5.so.2")
  2. CLR wraps it: TypeInitializationException("..GssInitializer", innerException: DllNotFoundException)
  3. This propagates from GssInitializer.Initialize() back into NetSecurityNative's cctor, which fails
  4. CLR wraps again: TypeInitializationException("..NetSecurityNative", innerException: TypeInitializationException("..GssInitializer", ...))

So tie.InnerException is TypeInitializationException (for GssInitializer), not DllNotFoundException. The DllNotFoundException is two levels deep at tie.InnerException.InnerException. The when guard evaluates to false and the catch block never executes.

This is confirmed by src/coreclr/vm/methodtable.cpp:3723-3751: RunClassInitEx captures the exception thrown by the cctor, and CreateTypeInitializationExceptionObject wraps it in a new TypeInitializationException — this wrapping happens independently for each type whose cctor fails.

Note: The pre-existing CheckHasSystemNetSecurityNative() (line 787) catches any TypeInitializationException without filtering on InnerException type, so it works correctly. The issue is only with the two Create method catch handlers.

If this concern is valid, a fix could be:

catch (TypeInitializationException tie) when (
    tie.InnerException is DllNotFoundException ||
    (tie.InnerException is TypeInitializationException tie2 && tie2.InnerException is DllNotFoundException))

Before this PR, this error path was also unhandled (the old InvalidOperationException wasn't caught either), so the PR is at worst no worse than before. But the intent seems to be to handle this case gracefully.

✅ Native code change — Correct and well-scoped

File: pal_gssapi.c line 125

Only the dlopen failure path had its fprintf(stderr, ...) removed. The dlsym failure path (line 138, PER_FUNCTION_BLOCK macro) correctly retains stderr logging — a loaded library missing expected symbols is unexpected and warrants a diagnostic message. Good distinction.

✅ Exception type change — Appropriate

File: Interop.NetSecurityNative.IsNtlmInstalled.cs line 31

Changing from InvalidOperationException to DllNotFoundException(GssApiLibraryName) is the right call. DllNotFoundException accurately describes the failure (the GSSAPI library couldn't be loaded), enables more specific catch filters, and includes the library name for diagnostics.

✅ Logging in CheckHasSystemNetSecurityNative — Correct

File: NegotiateAuthenticationPal.Unix.cs lines 789-790

The logging improvement correctly unwraps TypeInitializationException to log the meaningful inner exception, and the broad TypeInitializationException catch (without a DllNotFoundException-specific guard) correctly handles the double-wrapped case.

💡 Test helper has similar pattern without logging

File: src/libraries/Common/tests/System/Net/Capability.Security.Unix.cs line 31

The test helper CheckHasSystemNetSecurityNative() has the same broad TypeInitializationException catch but doesn't log. This is fine for test code — just noting the parallel exists.

✅ No new public API surface

No changes to ref/ assembly files or new public members. API approval verification is not needed.

Generated by Code Review for issue #126300 ·

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

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Runtime logs to stderr if libgssapi_krb5 is not available

4 participants