Skip to content

Commit 123b59a

Browse files
committed
Ensure planning encodes and scans cannot infinitely recurse
#2141
1 parent a95cfbb commit 123b59a

File tree

3 files changed

+27
-10
lines changed

3 files changed

+27
-10
lines changed

pgtype/json.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ func (c *JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanP
130130
// https://github.com/jackc/pgx/issues/1691 -- ** anything else
131131

132132
if wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {
133-
if nextPlan := m.planScan(oid, format, nextDst); nextPlan != nil {
133+
if nextPlan := m.planScan(oid, format, nextDst, 0); nextPlan != nil {
134134
if _, failed := nextPlan.(*scanPlanFail); !failed {
135135
wrapperPlan.SetNext(nextPlan)
136136
return wrapperPlan

pgtype/pgtype.go

+25-8
Original file line numberDiff line numberDiff line change
@@ -449,14 +449,14 @@ func (plan *scanPlanFail) Scan(src []byte, dst any) error {
449449
// As a horrible hack try all types to find anything that can scan into dst.
450450
for oid := range plan.m.oidToType {
451451
// using planScan instead of Scan or PlanScan to avoid polluting the planned scan cache.
452-
plan := plan.m.planScan(oid, plan.formatCode, dst)
452+
plan := plan.m.planScan(oid, plan.formatCode, dst, 0)
453453
if _, ok := plan.(*scanPlanFail); !ok {
454454
return plan.Scan(src, dst)
455455
}
456456
}
457457
for oid := range defaultMap.oidToType {
458458
if _, ok := plan.m.oidToType[oid]; !ok {
459-
plan := plan.m.planScan(oid, plan.formatCode, dst)
459+
plan := plan.m.planScan(oid, plan.formatCode, dst, 0)
460460
if _, ok := plan.(*scanPlanFail); !ok {
461461
return plan.Scan(src, dst)
462462
}
@@ -1064,6 +1064,14 @@ func (plan *wrapPtrArrayReflectScanPlan) Scan(src []byte, target any) error {
10641064

10651065
// PlanScan prepares a plan to scan a value into target.
10661066
func (m *Map) PlanScan(oid uint32, formatCode int16, target any) ScanPlan {
1067+
return m.planScanDepth(oid, formatCode, target, 0)
1068+
}
1069+
1070+
func (m *Map) planScanDepth(oid uint32, formatCode int16, target any, depth int) ScanPlan {
1071+
if depth > 8 {
1072+
return &scanPlanFail{m: m, oid: oid, formatCode: formatCode}
1073+
}
1074+
10671075
oidMemo := m.memoizedScanPlans[oid]
10681076
if oidMemo == nil {
10691077
oidMemo = make(map[reflect.Type][2]ScanPlan)
@@ -1073,15 +1081,15 @@ func (m *Map) PlanScan(oid uint32, formatCode int16, target any) ScanPlan {
10731081
typeMemo := oidMemo[targetReflectType]
10741082
plan := typeMemo[formatCode]
10751083
if plan == nil {
1076-
plan = m.planScan(oid, formatCode, target)
1084+
plan = m.planScan(oid, formatCode, target, depth)
10771085
typeMemo[formatCode] = plan
10781086
oidMemo[targetReflectType] = typeMemo
10791087
}
10801088

10811089
return plan
10821090
}
10831091

1084-
func (m *Map) planScan(oid uint32, formatCode int16, target any) ScanPlan {
1092+
func (m *Map) planScan(oid uint32, formatCode int16, target any, depth int) ScanPlan {
10851093
if target == nil {
10861094
return &scanPlanFail{m: m, oid: oid, formatCode: formatCode}
10871095
}
@@ -1141,7 +1149,7 @@ func (m *Map) planScan(oid uint32, formatCode int16, target any) ScanPlan {
11411149

11421150
for _, f := range m.TryWrapScanPlanFuncs {
11431151
if wrapperPlan, nextDst, ok := f(target); ok {
1144-
if nextPlan := m.planScan(oid, formatCode, nextDst); nextPlan != nil {
1152+
if nextPlan := m.planScanDepth(oid, formatCode, nextDst, depth+1); nextPlan != nil {
11451153
if _, failed := nextPlan.(*scanPlanFail); !failed {
11461154
wrapperPlan.SetNext(nextPlan)
11471155
return wrapperPlan
@@ -1201,6 +1209,15 @@ func codecDecodeToTextFormat(codec Codec, m *Map, oid uint32, format int16, src
12011209
// PlanEncode returns an Encode plan for encoding value into PostgreSQL format for oid and format. If no plan can be
12021210
// found then nil is returned.
12031211
func (m *Map) PlanEncode(oid uint32, format int16, value any) EncodePlan {
1212+
return m.planEncodeDepth(oid, format, value, 0)
1213+
}
1214+
1215+
func (m *Map) planEncodeDepth(oid uint32, format int16, value any, depth int) EncodePlan {
1216+
// Guard against infinite recursion.
1217+
if depth > 8 {
1218+
return nil
1219+
}
1220+
12041221
oidMemo := m.memoizedEncodePlans[oid]
12051222
if oidMemo == nil {
12061223
oidMemo = make(map[reflect.Type][2]EncodePlan)
@@ -1210,15 +1227,15 @@ func (m *Map) PlanEncode(oid uint32, format int16, value any) EncodePlan {
12101227
typeMemo := oidMemo[targetReflectType]
12111228
plan := typeMemo[format]
12121229
if plan == nil {
1213-
plan = m.planEncode(oid, format, value)
1230+
plan = m.planEncode(oid, format, value, depth)
12141231
typeMemo[format] = plan
12151232
oidMemo[targetReflectType] = typeMemo
12161233
}
12171234

12181235
return plan
12191236
}
12201237

1221-
func (m *Map) planEncode(oid uint32, format int16, value any) EncodePlan {
1238+
func (m *Map) planEncode(oid uint32, format int16, value any, depth int) EncodePlan {
12221239
if format == TextFormatCode {
12231240
switch value.(type) {
12241241
case string:
@@ -1249,7 +1266,7 @@ func (m *Map) planEncode(oid uint32, format int16, value any) EncodePlan {
12491266

12501267
for _, f := range m.TryWrapEncodePlanFuncs {
12511268
if wrapperPlan, nextValue, ok := f(value); ok {
1252-
if nextPlan := m.PlanEncode(oid, format, nextValue); nextPlan != nil {
1269+
if nextPlan := m.planEncodeDepth(oid, format, nextValue, depth+1); nextPlan != nil {
12531270
wrapperPlan.SetNext(nextPlan)
12541271
return wrapperPlan
12551272
}

pgtype/xml.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (c *XMLCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPl
113113
// https://github.com/jackc/pgx/issues/1691 -- ** anything else
114114

115115
if wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {
116-
if nextPlan := m.planScan(oid, format, nextDst); nextPlan != nil {
116+
if nextPlan := m.planScan(oid, format, nextDst, 0); nextPlan != nil {
117117
if _, failed := nextPlan.(*scanPlanFail); !failed {
118118
wrapperPlan.SetNext(nextPlan)
119119
return wrapperPlan

0 commit comments

Comments
 (0)