Skip to content

Commit

Permalink
Merge pull request #3203 from square/amorde/ios-buffer-overflow-fix
Browse files Browse the repository at this point in the history
Fix buffer overflow and data corruption when a type has more than 5 layers of nesting
  • Loading branch information
amorde authored Dec 6, 2024
2 parents 67b73b5 + e68fe4c commit 01e8f30
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -708,13 +708,13 @@ public final class ProtoWriter {
}

if let isProto3 = isProto3 {
messageStackIndex += 1
if messageStackIndex >= messageStackCapacity {
if messageStackIndex + 1 >= messageStackCapacity {
expandMessageStack()
}
let frame = MessageFrame(
isProto3: isProto3
)
messageStackIndex += 1
messageStack.advanced(by: messageStackIndex).initialize(to: frame)

// Cache this value as an ivar for quick access.
Expand Down
3 changes: 3 additions & 0 deletions wire-runtime-swift/src/test/proto/nested4.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
* limitations under the License.
*/
syntax = "proto2";
import "nested5.proto";

message Nested4 {
required string name = 1;

optional Nested5 nested = 2;
}

25 changes: 25 additions & 0 deletions wire-runtime-swift/src/test/proto/nested5.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2024 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto2";

import "nested6.proto";

message Nested5 {
required string name = 1;

optional Nested6 nested = 2;
}

21 changes: 21 additions & 0 deletions wire-runtime-swift/src/test/proto/nested6.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2024 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto2";

message Nested6 {
required string name = 1;
}

50 changes: 50 additions & 0 deletions wire-runtime-swift/src/test/swift/ProtoWriterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,56 @@ final class ProtoWriterTests: XCTestCase {
""")
}

func testEncodeMessageWithStackExpansion() throws {
let writer = ProtoWriter()
let message = Nested1(name: "name1") { v1 in
v1.nested = Nested2(name: "name2") { v2 in
v2.nested = Nested3(name: "name3") { v3 in
v3.nested = Nested4(name: "name4") { v4 in
v4.nested = Nested5(name: "name5") { v5 in
v5.nested = Nested6(name: "name6")
}
}
}
}
}

try writer.encode(tag: 1, value: message)

assertBufferEqual(writer, """
0A // (Tag 1 | Length Delimited)
34 // Length 52 for Nested1
0A // (Tag 1 | Length Delimited)
05 // Length 5 for name1
6E616D6531 // UTF-8 Value "name1"
12 // (Tag 2 | Length Delimited)
2B // Length 43
0A // (Tag 1 | Length Delimited)
05 // Length 5 for name2
6E616D6532 // UTF-8 Value "name2"
12 // (Tag 2 | Length Delimited)
22 // Length 34
0A // (Tag 1 | Length Delimited)
05 // Length 5 for name3
6E616D6533 // UTF-8 Value "name3"
12 // (Tag 2 | Length Delimited)
19 // Length 25
0A // (Tag 1 | Length Delimited)
05 // Length 5 for name4
6E616D6534 // UTF-8 Value "name4"
12 // (Tag 2 | Length Delimited)
10 // Length 16
0A // (Tag 1 | Length Delimited)
05 // Length 5 for name5
6E616D6535 // UTF-8 Value "name5"
12 // (Tag 2 | Length Delimited)
07 // Length 7
0A // (Tag 1 | Length Delimited)
05 // Length 5 for name6
6E616D6536 // UTF-8 Value "name6"
""")
}

func testEncodeString() throws {
let writer = ProtoWriter()
try writer.encode(tag: 1, value: "foo")
Expand Down

0 comments on commit 01e8f30

Please sign in to comment.