You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by je...@apache.org on 2026/04/15 22:07:20 UTC
(thrift) 05/09: Add input validation to Swift protocol layer Client: swift
This is an automated email from the ASF dual-hosted git repository.
jensg pushed a commit to branch 0.23.0
in repository https://gitbox.apache.org/repos/asf/thrift.git
commit e242889231df48f8791372598052862c28f5be83
Author: Jens Geyer <je...@apache.org>
AuthorDate: Sat Apr 11 12:29:23 2026 +0200
Add input validation to Swift protocol layer
Client: swift
- Add recursion depth limit (64) and negative size checks to
skip() in TProtocol for map, set, and list types
- Add range validation for fieldId before UInt8 cast in
TCompactProtocol.readFieldBegin()
(07, 08)
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
This closes #3392
---
lib/swift/Sources/TCompactProtocol.swift | 4 ++++
lib/swift/Sources/TProtocol.swift | 41 ++++++++++++++++++++++----------
2 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/lib/swift/Sources/TCompactProtocol.swift b/lib/swift/Sources/TCompactProtocol.swift
index 812df25c1..acdb91b79 100644
--- a/lib/swift/Sources/TCompactProtocol.swift
+++ b/lib/swift/Sources/TCompactProtocol.swift
@@ -313,6 +313,10 @@ public class TCompactProtocol: TProtocol {
booleanValue = type == .boolean_TRUE
}
+ guard fieldId >= 0 && fieldId <= Int16(UInt8.max) else {
+ throw TProtocolError(error: .invalidData,
+ message: "Field id out of range: \(fieldId)")
+ }
// push the new field onto the field stack so we can keep the deltas going
lastFieldId = UInt8(fieldId)
return ("", fieldType, Int32(fieldId))
diff --git a/lib/swift/Sources/TProtocol.swift b/lib/swift/Sources/TProtocol.swift
index ed6db6fa3..e3a7a0ec4 100644
--- a/lib/swift/Sources/TProtocol.swift
+++ b/lib/swift/Sources/TProtocol.swift
@@ -133,6 +133,14 @@ public extension TProtocol {
}
func skip(type: TType) throws {
+ try skip(type: type, depth: 0)
+ }
+
+ private func skip(type: TType, depth: Int) throws {
+ let nextDepth = depth + 1
+ if nextDepth > 64 {
+ throw TProtocolError(error: .depthLimit, message: "Maximum skip depth exceeded")
+ }
switch type {
case .bool: _ = try read() as Bool
case .i8: _ = try read() as Int8
@@ -142,7 +150,7 @@ public extension TProtocol {
case .double: _ = try read() as Double
case .string: _ = try read() as String
case .uuid: _ = try read() as UUID
-
+
case .struct:
_ = try readStructBegin()
while true {
@@ -150,35 +158,44 @@ public extension TProtocol {
if fieldType == .stop {
break
}
- try skip(type: fieldType)
+ try skip(type: fieldType, depth: nextDepth)
try readFieldEnd()
}
try readStructEnd()
-
-
+
+
case .map:
let (keyType, valueType, size) = try readMapBegin()
+ if size < 0 {
+ throw TProtocolError(error: .negativeSize, message: "Negative map size: \(size)")
+ }
for _ in 0..<size {
- try skip(type: keyType)
- try skip(type: valueType)
+ try skip(type: keyType, depth: nextDepth)
+ try skip(type: valueType, depth: nextDepth)
}
try readMapEnd()
-
-
+
+
case .set:
let (elemType, size) = try readSetBegin()
+ if size < 0 {
+ throw TProtocolError(error: .negativeSize, message: "Negative set size: \(size)")
+ }
for _ in 0..<size {
- try skip(type: elemType)
+ try skip(type: elemType, depth: nextDepth)
}
try readSetEnd()
-
+
case .list:
let (elemType, size) = try readListBegin()
+ if size < 0 {
+ throw TProtocolError(error: .negativeSize, message: "Negative list size: \(size)")
+ }
for _ in 0..<size {
- try skip(type: elemType)
+ try skip(type: elemType, depth: nextDepth)
}
try readListEnd()
-
+
default:
throw TProtocolError(error: .invalidData, message: "Invalid data")
}