Skip to content

Commit

Permalink
[Foundation] Don't leak exceptions in WrappedNSInputStream.Read.
Browse files Browse the repository at this point in the history
We don't want to leak exceptions back to the calling native code in WrappedNSInputStream.Read, because that will likely crash the process.

Example stack trace:

    ObjectDisposed_StreamClosed (System.ObjectDisposedException)
       at System.ThrowHelper.ThrowObjectDisposedException_StreamClosed(String) + 0x3c
       at System.IO.MemoryStream.Read(Byte[], Int32, Int32) + 0x124
       at System.Net.Http.MultipartContent.ContentReadStream.Read(Byte[], Int32, Int32) + 0x78
       at System.Net.Http.NSUrlSessionHandler.WrappedNSInputStream.Read(IntPtr buffer, UIntPtr len) + 0x58
       at MyApp!<BaseAddress>+0x7082f8

Instead return -1 from the Read method, which is documented as an error
condition, and then also return a custom NSError from the Error property -
which is also documented to be where the error is supposed to be surfaced.

Ref: https://developer.apple.com/documentation/foundation/nsinputstream/1411544-read

Ref: xamarin#20123.
  • Loading branch information
rolfbjarne committed Feb 16, 2024
1 parent da2525b commit 4262b24
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 11 deletions.
32 changes: 32 additions & 0 deletions src/Foundation/NSExceptionError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Runtime.Versioning;

#nullable enable

namespace Foundation {
#if NET
[SupportedOSPlatform ("ios")]
[SupportedOSPlatform ("maccatalyst")]
[SupportedOSPlatform ("macos")]
[SupportedOSPlatform ("tvos")]
#endif
public class NSExceptionError : NSError {
Exception exception;

public Exception Exception { get => exception; }

public NSExceptionError (Exception exception)
: base ((NSString) exception.GetType ().FullName, exception.HResult, GetDictionary (exception))
{
this.exception = exception;
}

static NSDictionary GetDictionary (Exception e)
{
var dict = new NSMutableDictionary ();
dict [NSError.LocalizedDescriptionKey] = (NSString) e.Message;
dict [NSError.LocalizedFailureReasonErrorKey] = (NSString) e.Message;
return dict;
}
}
}
36 changes: 25 additions & 11 deletions src/Foundation/NSUrlSessionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,7 @@ class WrappedNSInputStream : NSInputStream {
CFRunLoopSource source;
readonly Stream stream;
bool notifying;
NSError? error;

public WrappedNSInputStream (Stream inputStream)
{
Expand Down Expand Up @@ -1456,21 +1457,34 @@ public override void Close ()
[Preserve (Conditional = true)]
public override nint Read (IntPtr buffer, nuint len)
{
var sourceBytes = new byte [len];
var read = stream.Read (sourceBytes, 0, (int) len);
Marshal.Copy (sourceBytes, 0, buffer, (int) len);
try {
var sourceBytes = new byte [len];
var read = stream.Read (sourceBytes, 0, (int) len);
Marshal.Copy (sourceBytes, 0, buffer, (int) len);

if (notifying)
return read;
if (notifying)
return read;

notifying = true;
if (stream.CanSeek && stream.Position == stream.Length) {
Notify (CFStreamEventType.EndEncountered);
status = NSStreamStatus.AtEnd;
notifying = true;
if (stream.CanSeek && stream.Position == stream.Length) {
Notify (CFStreamEventType.EndEncountered);
status = NSStreamStatus.AtEnd;
}
notifying = false;

return read;
} catch (Exception e) {
// -1 means that the operation failed; more information about the error can be obtained with streamError.
error = new NSExceptionError (e);
return -1;
}
notifying = false;
}

return read;
[Preserve (Conditional = true)]
public override NSError Error {
get {
return error ?? base.Error;
}
}

[Preserve (Conditional = true)]
Expand Down
1 change: 1 addition & 0 deletions src/frameworks.sources
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,7 @@ FOUNDATION_SOURCES = \
Foundation/NSDistributedNotificationCenter.cs \
Foundation/NSEnumerator_1.cs \
Foundation/NSErrorException.cs \
Foundation/NSExceptionError.cs \
Foundation/NSExpression.cs \
Foundation/NSFastEnumerationState.cs \
Foundation/NSFastEnumerator.cs \
Expand Down

0 comments on commit 4262b24

Please sign in to comment.