|  | 
|  | 1 | +// Copyright (C) MongoDB, Inc. 2025-present. | 
|  | 2 | +// | 
|  | 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); you may | 
|  | 4 | +// not use this file except in compliance with the License. You may obtain | 
|  | 5 | +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 6 | + | 
|  | 7 | +package bson | 
|  | 8 | + | 
|  | 9 | +import ( | 
|  | 10 | +	"math" | 
|  | 11 | +	"strings" | 
|  | 12 | +	"testing" | 
|  | 13 | +) | 
|  | 14 | + | 
|  | 15 | +func FuzzDecodeValue(f *testing.F) { | 
|  | 16 | +	// Seed the fuzz corpus with all BSON values from the MarshalValue test | 
|  | 17 | +	// cases. | 
|  | 18 | +	for _, tc := range marshalValueTestCases { | 
|  | 19 | +		f.Add(byte(tc.bsontype), tc.bytes) | 
|  | 20 | +	} | 
|  | 21 | + | 
|  | 22 | +	// Also seed the fuzz corpus with special values that we want to test. | 
|  | 23 | +	values := []any{ | 
|  | 24 | +		// int32, including max and min values. | 
|  | 25 | +		int32(0), int32(math.MaxInt32), int32(math.MinInt32), | 
|  | 26 | +		// int64, including max and min values. | 
|  | 27 | +		int64(0), int64(math.MaxInt64), int64(math.MinInt64), | 
|  | 28 | +		// string, including empty and large string. | 
|  | 29 | +		"", strings.Repeat("z", 10_000), | 
|  | 30 | +		// map | 
|  | 31 | +		map[string]any{"nested": []any{1, "two", map[string]any{"three": 3}}}, | 
|  | 32 | +		// array | 
|  | 33 | +		[]any{1, 2, 3, "four"}, | 
|  | 34 | +	} | 
|  | 35 | + | 
|  | 36 | +	for _, v := range values { | 
|  | 37 | +		typ, b, err := MarshalValue(v) | 
|  | 38 | +		if err != nil { | 
|  | 39 | +			f.Fatal(err) | 
|  | 40 | +		} | 
|  | 41 | +		f.Add(byte(typ), b) | 
|  | 42 | +	} | 
|  | 43 | + | 
|  | 44 | +	f.Fuzz(func(t *testing.T, bsonType byte, data []byte) { | 
|  | 45 | +		var v any | 
|  | 46 | +		if err := UnmarshalValue(Type(bsonType), data, &v); err != nil { | 
|  | 47 | +			return | 
|  | 48 | +		} | 
|  | 49 | + | 
|  | 50 | +		// There is no value encoder for Go "nil" (nil values handled | 
|  | 51 | +		// differently by each type encoder), so skip anything that unmarshals | 
|  | 52 | +		// to "nil". It's not clear if MarshalValue should support "nil", but | 
|  | 53 | +		// for now we skip it. | 
|  | 54 | +		if v == nil { | 
|  | 55 | +			t.Logf("data unmarshaled to nil: %v", data) | 
|  | 56 | +			return | 
|  | 57 | +		} | 
|  | 58 | + | 
|  | 59 | +		typ, encoded, err := MarshalValue(v) | 
|  | 60 | +		if err != nil { | 
|  | 61 | +			t.Fatalf("failed to marshal: %v", err) | 
|  | 62 | +		} | 
|  | 63 | + | 
|  | 64 | +		var v2 any | 
|  | 65 | +		if err := UnmarshalValue(typ, encoded, &v2); err != nil { | 
|  | 66 | +			t.Fatalf("failed to unmarshal: %v", err) | 
|  | 67 | +		} | 
|  | 68 | +	}) | 
|  | 69 | +} | 
0 commit comments