From de4a23ac3d178f27ac7b5c4ee5a4a85db509ec14 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Mon, 18 Aug 2025 17:23:06 +0200 Subject: [PATCH 1/2] Fix StackOverflowException on StringUtil.Create `Span buffer = stackalloc byte[(int)sequence.Length];` --- Src/SmtpServer/Protocol/SmtpParser.cs | 20 ++++++++++++++++---- Src/SmtpServer/Text/StringUtil.cs | 5 +++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Src/SmtpServer/Protocol/SmtpParser.cs b/Src/SmtpServer/Protocol/SmtpParser.cs index 38018cb..3b57b32 100644 --- a/Src/SmtpServer/Protocol/SmtpParser.cs +++ b/Src/SmtpServer/Protocol/SmtpParser.cs @@ -1099,13 +1099,13 @@ public bool TryMakeMailbox(ref TokenReader reader, out IMailbox mailbox) if (reader.TryMake(TryMakeDomain, out var domain)) { - mailbox = CreateMailbox(localpart, domain); + mailbox = CreateMailbox(localpart, domain) ?? throw new SmtpResponseException(SmtpResponse.MailboxNameNotAllowed); return true; } if (reader.TryMake(TryMakeAddressLiteral, out var address)) { - mailbox = CreateMailbox(localpart, address); + mailbox = CreateMailbox(localpart, address) ?? throw new SmtpResponseException(SmtpResponse.MailboxNameNotAllowed); return true; } @@ -1113,9 +1113,21 @@ public bool TryMakeMailbox(ref TokenReader reader, out IMailbox mailbox) static Mailbox CreateMailbox(ReadOnlySequence localpart, ReadOnlySequence domainOrAddress) { - var user = Regex.Unescape(StringUtil.Create(localpart, Encoding.UTF8).Trim('"')); + var tempLocalpart = StringUtil.Create(localpart, Encoding.UTF8)?.Trim('"'); + if (tempLocalpart == null) + { + return null; + } + + var tempDomain = StringUtil.Create(domainOrAddress); + if (tempDomain == null) + { + return null; + } + + var unescapedLocalpart = Regex.Unescape(tempLocalpart); - return new Mailbox(user, StringUtil.Create(domainOrAddress)); + return new Mailbox(unescapedLocalpart, tempDomain); } } diff --git a/Src/SmtpServer/Text/StringUtil.cs b/Src/SmtpServer/Text/StringUtil.cs index 0e097b6..e0ed3ed 100644 --- a/Src/SmtpServer/Text/StringUtil.cs +++ b/Src/SmtpServer/Text/StringUtil.cs @@ -18,6 +18,11 @@ internal static unsafe string Create(ReadOnlySequence sequence, Encoding e return null; } + if (sequence.Length > ushort.MaxValue) + { + return null; + } + if (sequence.IsSingleSegment) { var span = sequence.First.Span; From f00fe50f0b925b00f1bc7d5d76641a4c08be6454 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Thu, 21 Aug 2025 17:05:11 +0200 Subject: [PATCH 2/2] Refactor StringUtil.Create to remove unsafe code Replaces unsafe pointer usage in StringUtil.Create with safe Encoding.GetString overloads. Also changes length check from ushort.MaxValue to short.MaxValue for consistency. Encoding.GetString(ReadOnlySpan) available with .NET Standard 2.1 --- Src/SmtpServer/Text/StringUtil.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Src/SmtpServer/Text/StringUtil.cs b/Src/SmtpServer/Text/StringUtil.cs index e0ed3ed..d2912ff 100644 --- a/Src/SmtpServer/Text/StringUtil.cs +++ b/Src/SmtpServer/Text/StringUtil.cs @@ -11,26 +11,21 @@ internal static string Create(ReadOnlySequence sequence) return Create(sequence, Encoding.ASCII); } - internal static unsafe string Create(ReadOnlySequence sequence, Encoding encoding) + internal static string Create(ReadOnlySequence sequence, Encoding encoding) { if (sequence.Length == 0) { return null; } - if (sequence.Length > ushort.MaxValue) + if (sequence.Length > short.MaxValue) { return null; } if (sequence.IsSingleSegment) { - var span = sequence.First.Span; - - fixed (byte* ptr = span) - { - return encoding.GetString(ptr, span.Length); - } + return encoding.GetString(sequence.First.Span); } else { @@ -48,10 +43,7 @@ internal static unsafe string Create(ReadOnlySequence sequence, Encoding e } } - fixed (byte* ptr = buffer) - { - return encoding.GetString(ptr, buffer.Length); - } + return encoding.GetString(buffer); } }