[Foundation] Don't leak exceptions in WrappedNSInputStream.Read. (#20131)
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: https://github.com/xamarin/xamarin-macios/issues/20123.
This commit is contained in:
Родитель
16be59f7d6
Коммит
79ac366c8f
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Foundation {
|
||||
#if NET
|
||||
[SupportedOSPlatform ("ios")]
|
||||
[SupportedOSPlatform ("maccatalyst")]
|
||||
[SupportedOSPlatform ("macos")]
|
||||
[SupportedOSPlatform ("tvos")]
|
||||
#endif
|
||||
public sealed 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;
|
||||
IsDirectBinding = false;
|
||||
}
|
||||
|
||||
static NSDictionary GetDictionary (Exception e)
|
||||
{
|
||||
var dict = new NSMutableDictionary ();
|
||||
dict [NSError.LocalizedDescriptionKey] = (NSString) e.Message;
|
||||
dict [NSError.LocalizedFailureReasonErrorKey] = (NSString) e.Message;
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1428,6 +1428,7 @@ namespace Foundation {
|
|||
CFRunLoopSource source;
|
||||
readonly Stream stream;
|
||||
bool notifying;
|
||||
NSError? error;
|
||||
|
||||
public WrappedNSInputStream (Stream inputStream)
|
||||
{
|
||||
|
@ -1456,21 +1457,34 @@ namespace Foundation {
|
|||
[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;
|
||||
|
||||
notifying = true;
|
||||
if (stream.CanSeek && stream.Position == stream.Length) {
|
||||
Notify (CFStreamEventType.EndEncountered);
|
||||
status = NSStreamStatus.AtEnd;
|
||||
}
|
||||
notifying = false;
|
||||
|
||||
if (notifying)
|
||||
return read;
|
||||
|
||||
notifying = true;
|
||||
if (stream.CanSeek && stream.Position == stream.Length) {
|
||||
Notify (CFStreamEventType.EndEncountered);
|
||||
status = NSStreamStatus.AtEnd;
|
||||
} 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)]
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -322,6 +322,11 @@ namespace Introspection {
|
|||
if (!t.IsPublic || !NSObjectType.IsAssignableFrom (t))
|
||||
continue;
|
||||
|
||||
// we only care about wrapper types (types with a native counterpart), and they all have a Register attribute.
|
||||
var typeRegisterAttribute = t.GetCustomAttribute<RegisterAttribute> (false);
|
||||
if (typeRegisterAttribute is null)
|
||||
continue;
|
||||
|
||||
int designated = 0;
|
||||
foreach (var ctor in t.GetConstructors ()) {
|
||||
if (ctor.GetCustomAttribute<DesignatedInitializerAttribute> () is null)
|
||||
|
|
Загрузка…
Ссылка в новой задаче