Skip to content

Commit fbda32d

Browse files
committed
abi/fastly,configstore: protect valueBuf with a mutex
1 parent bdfaf9f commit fbda32d

File tree

5 files changed

+80
-32
lines changed

5 files changed

+80
-32
lines changed

configstore/configstore.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ func Open(name string) (*Store, error) {
6161
return &Store{d}, nil
6262
}
6363

64-
// Has checks to see if the item exists in the config store, without allocating
65-
// any space to read it.
64+
// Has returns true if the key exists in the config store, without allocating
65+
// space to read a value.
6666
func (s *Store) Has(key string) (bool, error) {
6767
if s == nil {
6868
return false, ErrKeyNotFound
@@ -86,7 +86,7 @@ func (s *Store) Has(key string) (bool, error) {
8686
return v, nil
8787
}
8888

89-
// GetBytes returns the item in the config store with the given key, as a byte slice.
89+
// GetBytes returns the value in the config store for the given key, if it exists, as a byte slice.
9090
func (s *Store) GetBytes(key string) ([]byte, error) {
9191
if s == nil {
9292
return nil, ErrKeyNotFound
@@ -106,17 +106,14 @@ func (s *Store) GetBytes(key string) ([]byte, error) {
106106
return nil, err
107107
}
108108
}
109-
p := make([]byte, len(v))
110-
copy(p, v)
111-
return p, nil
109+
return v, nil
112110
}
113111

114-
// Get returns the item in the config store with the given key.
112+
// Get returns the value in the config store with the given key, if it exists.
115113
func (s *Store) Get(key string) (string, error) {
116114
buf, err := s.GetBytes(key)
117115
if err != nil {
118116
return "", err
119117
}
120-
121118
return string(buf), nil
122119
}

integration_tests/config_store/configstore.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
{
22
"twitter": "https://twitter.com/fastly",
33
"empty-value": "",
4+
"concurrent 0": "0",
5+
"concurrent 1": "11",
6+
"concurrent 2": "222",
7+
"concurrent 3": "3333",
8+
"concurrent 4": "44444",
49
"maximum-length-asciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000-2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":
510
"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
611
"ゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝゝ":

integration_tests/config_store/main_test.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package main
66

77
import (
88
"bytes"
9+
"strconv"
910
"strings"
11+
"sync"
1012
"testing"
1113

1214
"github.com/fastly/compute-sdk-go/configstore"
@@ -62,7 +64,8 @@ func TestConfigStore(t *testing.T) {
6264
t.Errorf("Body = %q, want %q", got, want)
6365
}
6466

65-
max, err := d.Get("maximum-length-asciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000-2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
67+
maxKey := "maximum-length-asciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000-2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
68+
max, err := d.Get(maxKey)
6669
if err != nil {
6770
t.Fatal(err)
6871
}
@@ -83,3 +86,33 @@ func TestConfigStore(t *testing.T) {
8386
t.Errorf("Body = %q, want %q", got, want)
8487
}
8588
}
89+
90+
func TestConfigStoreConcurrently(t *testing.T) {
91+
d, err := configstore.Open("configstore")
92+
if err != nil {
93+
t.Fatal(err)
94+
}
95+
var wg sync.WaitGroup
96+
wg.Add(5)
97+
var keys [5]string
98+
var values [5][]byte
99+
for i := 0; i < 5; i++ {
100+
go func(i int) {
101+
defer wg.Done()
102+
var err error
103+
keys[i] = "concurrent " + strconv.Itoa(i)
104+
values[i], err = d.GetBytes(keys[i])
105+
if err != nil {
106+
t.Errorf("%d: GetBytes() error: %v", i, err)
107+
}
108+
}(i)
109+
}
110+
wg.Wait()
111+
for i := 0; i < 5; i++ {
112+
got := values[i]
113+
want := bytes.Repeat([]byte{'0' + byte(i)}, i+1)
114+
if !bytes.Equal(got, want) {
115+
t.Errorf("%d: got: %q want: %q", i, got, want)
116+
}
117+
}
118+
}

internal/abi/fastly/hostcalls_guest.go

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"math"
1212
"net"
1313
"strings"
14+
"sync"
1415
"time"
1516

1617
"github.com/fastly/compute-sdk-go/internal/abi/prim"
@@ -2441,10 +2442,22 @@ func fastlyDictionaryOpen(
24412442
// allows us to use valueBuf scratch space between the guest and host to avoid
24422443
// allocations any larger than necessary, without locking.
24432444
type Dictionary struct {
2444-
h dictionaryHandle
2445-
valueBuf [dictionaryValueMaxLen]byte
2445+
h dictionaryHandle
2446+
2447+
mu sync.Mutex // protects valueBuf
2448+
valueBuf [dictionaryMaxValueLen]byte
24462449
}
24472450

2451+
// Dictionaries are subject to very specific limitations: 255 character keys and 8000 character values, utf-8 encoded.
2452+
// The current storage collation limits utf-8 representations to 3 bytes in length.
2453+
// https://docs.fastly.com/en/guides/about-edge-dictionaries#limitations-and-considerations
2454+
// https://dev.mysql.com/doc/refman/8.4/en/charset-unicode-utf8mb3.html
2455+
// https://en.wikipedia.org/wiki/UTF-8#Encoding
2456+
const (
2457+
dictionaryMaxKeyLen = 255 * 3 // known maximum size for config store keys: 755 bytes, for 255 3-byte utf-8 encoded characters
2458+
dictionaryMaxValueLen = 8000 * 3 // known maximum size for config store values: 24,000 bytes, for 8000 3-byte utf-8 encoded characters
2459+
)
2460+
24482461
// OpenDictionary returns a reference to the named dictionary, if it exists.
24492462
func OpenDictionary(name string) (*Dictionary, error) {
24502463
var d Dictionary
@@ -2481,13 +2494,18 @@ func fastlyDictionaryGet(
24812494
nWritten prim.Pointer[prim.Usize],
24822495
) FastlyStatus
24832496

2484-
// Get the value for key, as a byte slice, if it exists.
2485-
func (d *Dictionary) GetBytes(key string) ([]byte, error) {
2486-
keyBuffer := prim.NewReadBufferFromString(key).Wstring()
2487-
buf := prim.NewWriteBufferFromBytes(d.valueBuf[:])
2497+
// Get the value for key, if it exists. The returned slice's backing array is
2498+
// shared between multiple calls to getBytesUnlocked.
2499+
func (d *Dictionary) getBytesUnlocked(key string) ([]byte, error) {
2500+
keyBuffer := prim.NewReadBufferFromString(key)
2501+
if keyBuffer.Len() > dictionaryMaxKeyLen {
2502+
return nil, FastlyStatusInval.toError()
2503+
}
2504+
buf := prim.NewWriteBufferFromBytes(d.valueBuf[:]) // fresh slice of backing array
2505+
keyStr := keyBuffer.Wstring()
24882506
status := fastlyDictionaryGet(
24892507
d.h,
2490-
keyBuffer.Data, keyBuffer.Len,
2508+
keyStr.Data, keyStr.Len,
24912509
prim.ToPointer(buf.Char8Pointer()), buf.Cap(),
24922510
prim.ToPointer(buf.NPointer()),
24932511
)
@@ -2497,17 +2515,21 @@ func (d *Dictionary) GetBytes(key string) ([]byte, error) {
24972515
return buf.AsBytes(), nil
24982516
}
24992517

2500-
// Get the value for key, if it exists.
2501-
func (d *Dictionary) Get(key string) (string, error) {
2502-
buf, err := d.GetBytes(key)
2518+
// GetBytes returns a slice of newly-allocated memory for the value
2519+
// corresponding to key.
2520+
func (d *Dictionary) GetBytes(key string) ([]byte, error) {
2521+
d.mu.Lock()
2522+
defer d.mu.Unlock()
2523+
v, err := d.getBytesUnlocked(key)
25032524
if err != nil {
2504-
return "", err
2525+
return nil, err
25052526
}
2506-
2507-
return string(buf), nil
2527+
p := make([]byte, len(v))
2528+
copy(p, v)
2529+
return p, nil
25082530
}
25092531

2510-
// Has returns whether a value exists.
2532+
// Has returns true if key is found.
25112533
func (d *Dictionary) Has(key string) (bool, error) {
25122534
keyBuffer := prim.NewReadBufferFromString(key).Wstring()
25132535
var npointer prim.Usize = 0

internal/abi/fastly/types.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,6 @@ const (
188188
ipBufLen = 16 // known size for IP address buffers
189189
dnsBufLen = 256 // known size for "DNS" values, enough to hold the longest possible hostname or domain name
190190

191-
// Dictionaries are subject to very specific limitations: 255 character keys and 8000 character values, utf-8 encoded.
192-
// The current storage collation limits utf-8 representations to 3 bytes in length.
193-
// https://docs.fastly.com/en/guides/about-edge-dictionaries#limitations-and-considerations
194-
// https://dev.mysql.com/doc/refman/8.4/en/charset-unicode-utf8mb3.html
195-
// https://en.wikipedia.org/wiki/UTF-8#Encoding
196-
dictionaryMaxKeyLen = 255 * 3 // known maximum size for config store keys: 755 bytes, for 255 3-byte utf-8 encoded characters
197-
dictionaryValueASCIIMaxLen = 8000 // known for 8000 ASCII-range utf-8 encoded characters
198-
dictionaryValueMaxLen = 8000 * 3 // known maximum size for config store values: 24,000 bytes, for 8000 3-byte utf-8 encoded characters
199-
200191
DefaultSmallBufLen = 128 // default size for "typically-small" values with variable sizes: HTTP methods, header names, tls protocol names, cipher suites
201192
DefaultMediumBufLen = 1024 // default size for values between small and large, with variable sizes
202193
DefaultLargeBufLen = 8192 // default size for "typically-large" values with variable sizes; header values, URLs.

0 commit comments

Comments
 (0)