Skip to content

Commit 452d8e4

Browse files
committed
Merge #523: scratch: add stack frame support
6fe5043 scratch: add stack frame support (Andrew Poelstra) Pull request description: Replaces the single-blob stack space ith one that internally manages multiple blobs, which are exposed to the user as "frames". Users allocate new blobs with `secp256k1_scratch_allocate_frame` and deallocate them with `secp256k1_scratch_deallocate_frame`. Then any calls to `secp256k1_scratch_alloc` use the frame at the top of the stack. This is guaranteed to succeed, assuming that the frame allocation succeeded and that the user is not requesting more memory than the frame was allocated with. Tree-SHA512: 0b2072c5b9df8f3b40fb6d76e94fcfcc6a03a7da33e31249b5f24b02eb8a3311f282f6a4732153d6101968de8f9a568009a72735a1cc688a0f3040055799a09d
2 parents 9bc2e26 + 6fe5043 commit 452d8e4

File tree

8 files changed

+92
-76
lines changed

8 files changed

+92
-76
lines changed

include/secp256k1.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,10 @@ SECP256K1_API void secp256k1_context_set_error_callback(
260260
*
261261
* Returns: a newly created scratch space.
262262
* Args: ctx: an existing context object (cannot be NULL)
263-
* In: init_size: initial amount of memory to allocate
264-
* max_size: maximum amount of memory to allocate
263+
* In: max_size: maximum amount of memory to allocate
265264
*/
266265
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
267266
const secp256k1_context* ctx,
268-
size_t init_size,
269267
size_t max_size
270268
) SECP256K1_ARG_NONNULL(1);
271269

src/bench_ecmult.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ int main(int argc, char **argv) {
154154
/* Allocate stuff */
155155
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
156156
scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
157-
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size, scratch_size);
157+
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
158158
data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS);
159159
data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS);
160160
data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS);

src/ecmult_impl.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -525,10 +525,9 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, s
525525
return 1;
526526
}
527527

528-
if (!secp256k1_scratch_resize(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
528+
if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
529529
return 0;
530530
}
531-
secp256k1_scratch_reset(scratch);
532531
points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej));
533532
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar));
534533
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
@@ -543,10 +542,14 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, s
543542

544543
for (i = 0; i < n_points; i++) {
545544
secp256k1_ge point;
546-
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) return 0;
545+
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
546+
secp256k1_scratch_deallocate_frame(scratch);
547+
return 0;
548+
}
547549
secp256k1_gej_set_ge(&points[i], &point);
548550
}
549551
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
552+
secp256k1_scratch_deallocate_frame(scratch);
550553
return 1;
551554
}
552555

@@ -873,10 +876,9 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
873876
}
874877

875878
bucket_window = secp256k1_pippenger_bucket_window(n_points);
876-
if (!secp256k1_scratch_resize(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
879+
if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
877880
return 0;
878881
}
879-
secp256k1_scratch_reset(scratch);
880882
points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points));
881883
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars));
882884
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space));
@@ -896,6 +898,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
896898

897899
while (point_idx < n_points) {
898900
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
901+
secp256k1_scratch_deallocate_frame(scratch);
899902
return 0;
900903
}
901904
idx++;
@@ -919,6 +922,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
919922
for(i = 0; i < 1<<bucket_window; i++) {
920923
secp256k1_gej_clear(&buckets[i]);
921924
}
925+
secp256k1_scratch_deallocate_frame(scratch);
922926
return 1;
923927
}
924928

src/scratch.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,33 @@
77
#ifndef _SECP256K1_SCRATCH_
88
#define _SECP256K1_SCRATCH_
99

10+
#define SECP256K1_SCRATCH_MAX_FRAMES 5
11+
1012
/* The typedef is used internally; the struct name is used in the public API
1113
* (where it is exposed as a different typedef) */
1214
typedef struct secp256k1_scratch_space_struct {
13-
void *data;
14-
size_t offset;
15-
size_t init_size;
15+
void *data[SECP256K1_SCRATCH_MAX_FRAMES];
16+
size_t offset[SECP256K1_SCRATCH_MAX_FRAMES];
17+
size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES];
18+
size_t frame;
1619
size_t max_size;
1720
const secp256k1_callback* error_callback;
1821
} secp256k1_scratch;
1922

20-
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t init_size, size_t max_size);
23+
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size);
24+
2125
static void secp256k1_scratch_destroy(secp256k1_scratch* scratch);
2226

27+
/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */
28+
static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects);
29+
30+
/** Deallocates a stack frame */
31+
static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch);
32+
2333
/** Returns the maximum allocation the scratch space will allow */
2434
static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects);
2535

26-
/** Attempts to allocate so that there are `n` available bytes. Returns 1 on success, 0 on failure */
27-
static int secp256k1_scratch_resize(secp256k1_scratch* scratch, size_t n, size_t n_objects);
28-
29-
/** Returns a pointer into the scratch space or NULL if there is insufficient available space */
36+
/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */
3037
static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n);
3138

32-
/** Resets the returned pointer to the beginning of space */
33-
static void secp256k1_scratch_reset(secp256k1_scratch* scratch);
34-
3539
#endif

src/scratch_impl.h

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,10 @@
1515
* TODO: Determine this at configure time. */
1616
#define ALIGNMENT 16
1717

18-
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t init_size, size_t max_size) {
18+
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) {
1919
secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret));
2020
if (ret != NULL) {
21-
ret->data = checked_malloc(error_callback, init_size);
22-
if (ret->data == NULL) {
23-
free (ret);
24-
return NULL;
25-
}
26-
ret->offset = 0;
27-
ret->init_size = init_size;
21+
memset(ret, 0, sizeof(*ret));
2822
ret->max_size = max_size;
2923
ret->error_callback = error_callback;
3024
}
@@ -33,45 +27,60 @@ static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* err
3327

3428
static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) {
3529
if (scratch != NULL) {
36-
free(scratch->data);
30+
VERIFY_CHECK(scratch->frame == 0);
3731
free(scratch);
3832
}
3933
}
4034

4135
static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) {
42-
if (scratch->max_size <= objects * ALIGNMENT) {
36+
size_t i = 0;
37+
size_t allocated = 0;
38+
for (i = 0; i < scratch->frame; i++) {
39+
allocated += scratch->frame_size[i];
40+
}
41+
if (scratch->max_size - allocated <= objects * ALIGNMENT) {
4342
return 0;
4443
}
45-
return scratch->max_size - objects * ALIGNMENT;
44+
return scratch->max_size - allocated - objects * ALIGNMENT;
4645
}
4746

48-
static int secp256k1_scratch_resize(secp256k1_scratch* scratch, size_t n, size_t objects) {
49-
n += objects * ALIGNMENT;
50-
if (n > scratch->init_size && n <= scratch->max_size) {
51-
void *tmp = checked_realloc(scratch->error_callback, scratch->data, n);
52-
if (tmp == NULL) {
47+
static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) {
48+
VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES);
49+
50+
if (n <= secp256k1_scratch_max_allocation(scratch, objects)) {
51+
n += objects * ALIGNMENT;
52+
scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n);
53+
if (scratch->data[scratch->frame] == NULL) {
5354
return 0;
5455
}
55-
scratch->init_size = n;
56-
scratch->data = tmp;
56+
scratch->frame_size[scratch->frame] = n;
57+
scratch->offset[scratch->frame] = 0;
58+
scratch->frame++;
59+
return 1;
60+
} else {
61+
return 0;
5762
}
58-
return n <= scratch->max_size;
63+
}
64+
65+
static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) {
66+
VERIFY_CHECK(scratch->frame > 0);
67+
scratch->frame -= 1;
68+
free(scratch->data[scratch->frame]);
5969
}
6070

6171
static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) {
6272
void *ret;
73+
size_t frame = scratch->frame - 1;
6374
size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
64-
if (size + scratch->offset > scratch->init_size) {
75+
76+
if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) {
6577
return NULL;
6678
}
67-
ret = (void *) ((unsigned char *) scratch->data + scratch->offset);
79+
ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]);
6880
memset(ret, 0, size);
69-
scratch->offset += size;
70-
return ret;
71-
}
81+
scratch->offset[frame] += size;
7282

73-
static void secp256k1_scratch_reset(secp256k1_scratch* scratch) {
74-
scratch->offset = 0;
83+
return ret;
7584
}
7685

7786
#endif

src/secp256k1.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,9 @@ void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(co
115115
ctx->error_callback.data = data;
116116
}
117117

118-
secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t init_size, size_t max_size) {
118+
secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t max_size) {
119119
VERIFY_CHECK(ctx != NULL);
120-
ARG_CHECK(max_size >= init_size);
121-
122-
return secp256k1_scratch_create(&ctx->error_callback, init_size, max_size);
120+
return secp256k1_scratch_create(&ctx->error_callback, max_size);
123121
}
124122

125123
void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) {

src/tests.c

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -258,28 +258,31 @@ void run_scratch_tests(void) {
258258

259259
/* Test public API */
260260
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
261-
scratch = secp256k1_scratch_space_create(none, 100, 10);
262-
CHECK(scratch == NULL);
263-
CHECK(ecount == 1);
264-
265-
scratch = secp256k1_scratch_space_create(none, 100, 100);
266-
CHECK(scratch != NULL);
267-
CHECK(ecount == 1);
268-
secp256k1_scratch_space_destroy(scratch);
269261

270-
scratch = secp256k1_scratch_space_create(none, 100, 1000);
262+
scratch = secp256k1_scratch_space_create(none, 1000);
271263
CHECK(scratch != NULL);
272-
CHECK(ecount == 1);
264+
CHECK(ecount == 0);
273265

274266
/* Test internal API */
275267
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
276268
CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000);
277-
CHECK(secp256k1_scratch_resize(scratch, 50, 1) == 1); /* no-op */
278-
CHECK(secp256k1_scratch_resize(scratch, 200, 1) == 1);
279-
CHECK(secp256k1_scratch_resize(scratch, 950, 1) == 1);
280-
CHECK(secp256k1_scratch_resize(scratch, 1000, 1) == 0);
281-
CHECK(secp256k1_scratch_resize(scratch, 2000, 1) == 0);
269+
270+
/* Allocating 500 bytes with no frame fails */
271+
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
272+
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
273+
274+
/* ...but pushing a new stack frame does affect the max allocation */
275+
CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1 == 1));
276+
CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 500); /* 500 - ALIGNMENT */
277+
CHECK(secp256k1_scratch_alloc(scratch, 500) != NULL);
278+
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
279+
280+
CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1) == 0);
281+
282+
/* ...and this effect is undone by popping the frame */
283+
secp256k1_scratch_deallocate_frame(scratch);
282284
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
285+
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
283286

284287
/* cleanup */
285288
secp256k1_scratch_space_destroy(scratch);
@@ -2558,7 +2561,6 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
25582561
data.sc = sc;
25592562
data.pt = pt;
25602563
secp256k1_scalar_set_int(&szero, 0);
2561-
secp256k1_scratch_reset(scratch);
25622564

25632565
/* No points to multiply */
25642566
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0));
@@ -2590,7 +2592,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
25902592
CHECK(secp256k1_gej_is_infinity(&r));
25912593

25922594
/* Try to multiply 1 point, but scratch space is empty */
2593-
scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0, 0);
2595+
scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
25942596
CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
25952597
secp256k1_scratch_destroy(scratch_empty);
25962598

@@ -2816,15 +2818,16 @@ void test_ecmult_multi_pippenger_max_points(void) {
28162818
int bucket_window = 0;
28172819

28182820
for(; scratch_size < max_size; scratch_size+=256) {
2819-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, scratch_size);
2821+
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
28202822
CHECK(scratch != NULL);
28212823
n_points_supported = secp256k1_pippenger_max_points(scratch);
28222824
if (n_points_supported == 0) {
28232825
secp256k1_scratch_destroy(scratch);
28242826
continue;
28252827
}
28262828
bucket_window = secp256k1_pippenger_bucket_window(n_points_supported);
2827-
CHECK(secp256k1_scratch_resize(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
2829+
CHECK(secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
2830+
secp256k1_scratch_deallocate_frame(scratch);
28282831
secp256k1_scratch_destroy(scratch);
28292832
}
28302833
CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);
@@ -2866,13 +2869,13 @@ void test_ecmult_multi_batching(void) {
28662869
data.pt = pt;
28672870

28682871
/* Test with empty scratch space */
2869-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, 0);
2872+
scratch = secp256k1_scratch_create(&ctx->error_callback, 0);
28702873
CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
28712874
secp256k1_scratch_destroy(scratch);
28722875

28732876
/* Test with space for 1 point in pippenger. That's not enough because
28742877
* ecmult_multi selects strauss which requires more memory. */
2875-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
2878+
scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
28762879
CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
28772880
secp256k1_scratch_destroy(scratch);
28782881

@@ -2881,10 +2884,10 @@ void test_ecmult_multi_batching(void) {
28812884
if (i > ECMULT_PIPPENGER_THRESHOLD) {
28822885
int bucket_window = secp256k1_pippenger_bucket_window(i);
28832886
size_t scratch_size = secp256k1_pippenger_scratch_size(i, bucket_window);
2884-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
2887+
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
28852888
} else {
28862889
size_t scratch_size = secp256k1_strauss_scratch_size(i);
2887-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
2890+
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
28882891
}
28892892
CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
28902893
secp256k1_gej_add_var(&r, &r, &r2, NULL);
@@ -2900,14 +2903,14 @@ void run_ecmult_multi_tests(void) {
29002903

29012904
test_secp256k1_pippenger_bucket_window_inv();
29022905
test_ecmult_multi_pippenger_max_points();
2903-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, 819200);
2906+
scratch = secp256k1_scratch_create(&ctx->error_callback, 819200);
29042907
test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
29052908
test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single);
29062909
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
29072910
secp256k1_scratch_destroy(scratch);
29082911

29092912
/* Run test_ecmult_multi with space for exactly one point */
2910-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
2913+
scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
29112914
test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
29122915
secp256k1_scratch_destroy(scratch);
29132916

src/tests_exhaustive.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t
196196

197197
void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
198198
int i, j, k, x, y;
199-
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 1024, 4096);
199+
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 4096);
200200
for (i = 0; i < order; i++) {
201201
for (j = 0; j < order; j++) {
202202
for (k = 0; k < order; k++) {

0 commit comments

Comments
 (0)