Skip to content

Commit 413bec0

Browse files
committed
Added bounds check to pk and checksum to payload
1 parent f3b47ab commit 413bec0

File tree

5 files changed

+321
-102
lines changed

5 files changed

+321
-102
lines changed

src/cloudsync.c

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,14 @@ struct cloudsync_payload_context {
193193
#endif
194194

195195
typedef struct PACKED {
196-
uint32_t signature; // 'CLSY'
197-
uint8_t version; // protocol version
198-
uint8_t libversion[3]; // major.minor.patch
196+
uint32_t signature; // 'CLSY'
197+
uint8_t version; // protocol version
198+
uint8_t libversion[3]; // major.minor.patch
199199
uint32_t expanded_size;
200200
uint16_t ncols;
201201
uint32_t nrows;
202202
uint64_t schema_hash;
203-
uint8_t unused[6]; // padding to ensure the struct is exactly 32 bytes
203+
uint8_t checksum[6]; // 48 bits checksum (to ensure struct is 32 bytes)
204204
} cloudsync_payload_header;
205205

206206
#ifdef _MSC_VER
@@ -1949,6 +1949,31 @@ int local_update_move_meta (cloudsync_table_context *table, const char *pk, size
19491949

19501950
// MARK: - Payload Encode / Decode -
19511951

1952+
static void cloudsync_payload_checksum_store (cloudsync_payload_header *header, uint64_t checksum) {
1953+
uint64_t h = checksum & 0xFFFFFFFFFFFFULL; // keep 48 bits
1954+
header->checksum[0] = (uint8_t)(h >> 40);
1955+
header->checksum[1] = (uint8_t)(h >> 32);
1956+
header->checksum[2] = (uint8_t)(h >> 24);
1957+
header->checksum[3] = (uint8_t)(h >> 16);
1958+
header->checksum[4] = (uint8_t)(h >> 8);
1959+
header->checksum[5] = (uint8_t)(h >> 0);
1960+
}
1961+
1962+
static uint64_t cloudsync_payload_checksum_load (cloudsync_payload_header *header) {
1963+
return ((uint64_t)header->checksum[0] << 40) |
1964+
((uint64_t)header->checksum[1] << 32) |
1965+
((uint64_t)header->checksum[2] << 24) |
1966+
((uint64_t)header->checksum[3] << 16) |
1967+
((uint64_t)header->checksum[4] << 8) |
1968+
((uint64_t)header->checksum[5] << 0);
1969+
}
1970+
1971+
static bool cloudsync_payload_checksum_verify (cloudsync_payload_header *header, uint64_t checksum) {
1972+
uint64_t checksum1 = cloudsync_payload_checksum_load(header);
1973+
uint64_t checksum2 = checksum & 0xFFFFFFFFFFFFULL;
1974+
return (checksum1 == checksum2);
1975+
}
1976+
19521977
static bool cloudsync_payload_encode_check (cloudsync_payload_context *payload, size_t needed) {
19531978
if (payload->nrows == 0) needed += sizeof(cloudsync_payload_header);
19541979

@@ -2008,7 +2033,9 @@ int cloudsync_payload_encode_step (cloudsync_payload_context *payload, cloudsync
20082033
}
20092034

20102035
char *buffer = payload->buffer + payload->bused;
2011-
pk_encode((dbvalue_t **)argv, argc, buffer, false, NULL, data->skip_decode_idx);
2036+
size_t bsize = payload->balloc - payload->bused;
2037+
char *p = pk_encode((dbvalue_t **)argv, argc, buffer, false, &bsize, data->skip_decode_idx);
2038+
if (!p) cloudsync_set_error(data, "An error occurred while encoding payload", DBRES_ERROR);
20122039

20132040
// update buffer
20142041
payload->bused += breq;
@@ -2077,6 +2104,10 @@ int cloudsync_payload_encode_final (cloudsync_payload_context *payload, cloudsyn
20772104
zused = real_buffer_size;
20782105
}
20792106

2107+
// compute checksum of the buffer
2108+
uint64_t checksum = pk_checksum(zbuffer + header_size, zused);
2109+
cloudsync_payload_checksum_store(&header, checksum);
2110+
20802111
// copy header and data to SQLite BLOB
20812112
memcpy(zbuffer, &header, sizeof(cloudsync_payload_header));
20822113
int blob_size = zused + sizeof(cloudsync_payload_header);
@@ -2179,6 +2210,12 @@ int cloudsync_payload_apply (cloudsync_context *data, const char *payload, int b
21792210
const char *buffer = payload + sizeof(cloudsync_payload_header);
21802211
blen -= sizeof(cloudsync_payload_header);
21812212

2213+
// sanity check checksum
2214+
uint64_t checksum = pk_checksum(buffer, blen);
2215+
if (cloudsync_payload_checksum_verify(&header, checksum) == false) {
2216+
return cloudsync_set_error(data, "Error on cloudsync_payload_apply: invalid checksum", DBRES_MISUSE);
2217+
}
2218+
21822219
// check if payload is compressed
21832220
char *clone = NULL;
21842221
if (header.expanded_size != 0) {
@@ -2216,7 +2253,12 @@ int cloudsync_payload_apply (cloudsync_context *data, const char *payload, int b
22162253

22172254
for (uint32_t i=0; i<nrows; ++i) {
22182255
size_t seek = 0;
2219-
pk_decode((char *)buffer, blen, ncols, &seek, data->skip_decode_idx, cloudsync_payload_decode_callback, &decoded_context);
2256+
int res = pk_decode((char *)buffer, blen, ncols, &seek, data->skip_decode_idx, cloudsync_payload_decode_callback, &decoded_context);
2257+
if (res == -1) {
2258+
if (in_savepoint) database_rollback_savepoint(data, "cloudsync_payload_apply");
2259+
rc = DBRES_ERROR;
2260+
goto cleanup;
2261+
}
22202262
// n is the pk_decode return value, I don't think I should assert here because in any case the next databasevm_step would fail
22212263
// assert(n == ncols);
22222264

@@ -2301,6 +2343,7 @@ int cloudsync_payload_apply (cloudsync_context *data, const char *payload, int b
23012343
}
23022344
}
23032345

2346+
cleanup:
23042347
// cleanup vm
23052348
if (vm) databasevm_finalize(vm);
23062349

src/endian.h

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//
2+
// endian.h
3+
// cloudsync
4+
//
5+
// Created by Marco Bambini on 17/01/26.
6+
//
7+
8+
#ifndef __CLOUDSYNC_ENDIAN__
9+
#define __CLOUDSYNC_ENDIAN__
10+
11+
#include <stdint.h>
12+
13+
#if defined(_MSC_VER)
14+
#include <stdlib.h> // _byteswap_uint64
15+
#endif
16+
17+
// =======================================================
18+
// bswap64 - portable
19+
// =======================================================
20+
21+
static inline uint64_t bswap64_u64(uint64_t v) {
22+
#if defined(_MSC_VER)
23+
return _byteswap_uint64(v);
24+
25+
#elif defined(__has_builtin)
26+
#if __has_builtin(__builtin_bswap64)
27+
return __builtin_bswap64(v);
28+
#else
29+
return ((v & 0x00000000000000FFull) << 56) |
30+
((v & 0x000000000000FF00ull) << 40) |
31+
((v & 0x0000000000FF0000ull) << 24) |
32+
((v & 0x00000000FF000000ull) << 8) |
33+
((v & 0x000000FF00000000ull) >> 8) |
34+
((v & 0x0000FF0000000000ull) >> 24) |
35+
((v & 0x00FF000000000000ull) >> 40) |
36+
((v & 0xFF00000000000000ull) >> 56);
37+
#endif
38+
39+
#elif defined(__GNUC__) || defined(__clang__)
40+
return __builtin_bswap64(v);
41+
42+
#else
43+
return ((v & 0x00000000000000FFull) << 56) |
44+
((v & 0x000000000000FF00ull) << 40) |
45+
((v & 0x0000000000FF0000ull) << 24) |
46+
((v & 0x00000000FF000000ull) << 8) |
47+
((v & 0x000000FF00000000ull) >> 8) |
48+
((v & 0x0000FF0000000000ull) >> 24) |
49+
((v & 0x00FF000000000000ull) >> 40) |
50+
((v & 0xFF00000000000000ull) >> 56);
51+
#endif
52+
}
53+
54+
// =======================================================
55+
// Compile-time endianness detection
56+
// =======================================================
57+
58+
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
59+
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
60+
#define HOST_IS_LITTLE_ENDIAN 1
61+
#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
62+
#define HOST_IS_LITTLE_ENDIAN 0
63+
#endif
64+
#endif
65+
66+
// WebAssembly is currently defined as little-endian in all major toolchains
67+
#if !defined(HOST_IS_LITTLE_ENDIAN) && (defined(__wasm__) || defined(__EMSCRIPTEN__))
68+
#define HOST_IS_LITTLE_ENDIAN 1
69+
#endif
70+
71+
// Runtime fallback if unknown at compile-time
72+
static inline int host_is_little_endian_runtime (void) {
73+
const uint16_t x = 1;
74+
return *((const uint8_t*)&x) == 1;
75+
}
76+
77+
// =======================================================
78+
// Public API
79+
// =======================================================
80+
81+
static inline uint64_t host_to_be64 (uint64_t v) {
82+
#if defined(HOST_IS_LITTLE_ENDIAN)
83+
#if HOST_IS_LITTLE_ENDIAN
84+
return bswap64_u64(v);
85+
#else
86+
return v;
87+
#endif
88+
#else
89+
return host_is_little_endian_runtime() ? bswap64_u64(v) : v;
90+
#endif
91+
}
92+
93+
static inline uint64_t be64_to_host (uint64_t v) {
94+
// same operation (bswap if little-endian)
95+
return host_to_be64(v);
96+
}
97+
98+
#endif
99+

0 commit comments

Comments
 (0)