Skip to content

Commit 158edaa

Browse files
committed
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 <noreply@anthropic.com> This closes #3392
1 parent 73fbcfb commit 158edaa

2 files changed

Lines changed: 33 additions & 12 deletions

File tree

lib/swift/Sources/TCompactProtocol.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ public class TCompactProtocol: TProtocol {
313313
booleanValue = type == .boolean_TRUE
314314
}
315315

316+
guard fieldId >= 0 && fieldId <= Int16(UInt8.max) else {
317+
throw TProtocolError(error: .invalidData,
318+
message: "Field id out of range: \(fieldId)")
319+
}
316320
// push the new field onto the field stack so we can keep the deltas going
317321
lastFieldId = UInt8(fieldId)
318322
return ("", fieldType, Int32(fieldId))

lib/swift/Sources/TProtocol.swift

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ public extension TProtocol {
133133
}
134134

135135
func skip(type: TType) throws {
136+
try skip(type: type, depth: 0)
137+
}
138+
139+
private func skip(type: TType, depth: Int) throws {
140+
let nextDepth = depth + 1
141+
if nextDepth > 64 {
142+
throw TProtocolError(error: .depthLimit, message: "Maximum skip depth exceeded")
143+
}
136144
switch type {
137145
case .bool: _ = try read() as Bool
138146
case .i8: _ = try read() as Int8
@@ -142,43 +150,52 @@ public extension TProtocol {
142150
case .double: _ = try read() as Double
143151
case .string: _ = try read() as String
144152
case .uuid: _ = try read() as UUID
145-
153+
146154
case .struct:
147155
_ = try readStructBegin()
148156
while true {
149157
let (_, fieldType, _) = try readFieldBegin()
150158
if fieldType == .stop {
151159
break
152160
}
153-
try skip(type: fieldType)
161+
try skip(type: fieldType, depth: nextDepth)
154162
try readFieldEnd()
155163
}
156164
try readStructEnd()
157-
158-
165+
166+
159167
case .map:
160168
let (keyType, valueType, size) = try readMapBegin()
169+
if size < 0 {
170+
throw TProtocolError(error: .negativeSize, message: "Negative map size: \(size)")
171+
}
161172
for _ in 0..<size {
162-
try skip(type: keyType)
163-
try skip(type: valueType)
173+
try skip(type: keyType, depth: nextDepth)
174+
try skip(type: valueType, depth: nextDepth)
164175
}
165176
try readMapEnd()
166-
167-
177+
178+
168179
case .set:
169180
let (elemType, size) = try readSetBegin()
181+
if size < 0 {
182+
throw TProtocolError(error: .negativeSize, message: "Negative set size: \(size)")
183+
}
170184
for _ in 0..<size {
171-
try skip(type: elemType)
185+
try skip(type: elemType, depth: nextDepth)
172186
}
173187
try readSetEnd()
174-
188+
175189
case .list:
176190
let (elemType, size) = try readListBegin()
191+
if size < 0 {
192+
throw TProtocolError(error: .negativeSize, message: "Negative list size: \(size)")
193+
}
177194
for _ in 0..<size {
178-
try skip(type: elemType)
195+
try skip(type: elemType, depth: nextDepth)
179196
}
180197
try readListEnd()
181-
198+
182199
default:
183200
throw TProtocolError(error: .invalidData, message: "Invalid data")
184201
}

0 commit comments

Comments
 (0)