Skip to content

Commit cbe0c9e

Browse files
committed
add lock and don't direct access the synced data
1 parent 72b6826 commit cbe0c9e

File tree

9 files changed

+138
-92
lines changed

9 files changed

+138
-92
lines changed

include/aws/s3/private/s3_checksum_context.h

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "aws/s3/s3_client.h"
1010
#include <aws/common/byte_buf.h>
11+
#include <aws/common/mutex.h>
1112
#include <aws/common/ref_count.h>
1213

1314
struct aws_s3_meta_request_checksum_config_storage;
@@ -28,9 +29,13 @@ struct aws_s3_upload_request_checksum_context {
2829
enum aws_s3_checksum_algorithm algorithm;
2930
enum aws_s3_checksum_location location;
3031

31-
struct aws_byte_buf base64_checksum;
32-
/* The checksum already be calculated or not. */
33-
bool checksum_calculated;
32+
struct {
33+
/* Lock to make sure the checksum context is safe to be access from different threads. */
34+
struct aws_mutex lock;
35+
struct aws_byte_buf base64_checksum;
36+
/* The checksum already be calculated or not. */
37+
bool checksum_calculated;
38+
} synced_data;
3439

3540
/* Validation */
3641
size_t encoded_checksum_size;
@@ -96,8 +101,7 @@ struct aws_s3_upload_request_checksum_context *aws_s3_upload_request_checksum_co
96101
* @return true if checksum calculation is needed, false otherwise
97102
*/
98103
AWS_S3_API
99-
bool aws_s3_upload_request_checksum_context_should_calculate(
100-
const struct aws_s3_upload_request_checksum_context *context);
104+
bool aws_s3_upload_request_checksum_context_should_calculate(struct aws_s3_upload_request_checksum_context *context);
101105

102106
/**
103107
* Check if checksum should be added to HTTP headers.
@@ -122,15 +126,18 @@ bool aws_s3_upload_request_checksum_context_should_add_trailer(
122126
const struct aws_s3_upload_request_checksum_context *context);
123127

124128
/**
125-
* Get the checksum buffer to use for output.
126-
* Returns the internal buffer for storing the calculated checksum.
129+
* Encode the checksum to base64 and store it in the context.
130+
* This function is thread-safe and can be called from multiple threads.
131+
* Returns AWS_OP_SUCCESS on success, AWS_OP_ERR otherwise
127132
*
128133
* @param context The checksum context
129-
* @return Pointer to the checksum buffer, or NULL if context is invalid
134+
* @param raw_checksum_cursor the byte cursor to the raw checksum value.
135+
* @return AWS_OP_SUCCESS on success, AWS_OP_ERR otherwise
130136
*/
131137
AWS_S3_API
132-
struct aws_byte_buf *aws_s3_upload_request_checksum_context_get_output_buffer(
133-
struct aws_s3_upload_request_checksum_context *context);
138+
int aws_s3_upload_request_checksum_context_finalize_checksum(
139+
struct aws_s3_upload_request_checksum_context *context,
140+
struct aws_byte_cursor raw_checksum_cursor);
134141

135142
/**
136143
* Get a cursor to the current base64 encoded checksum value (for use in headers/trailers).
@@ -141,7 +148,7 @@ struct aws_byte_buf *aws_s3_upload_request_checksum_context_get_output_buffer(
141148
*/
142149
AWS_S3_API
143150
struct aws_byte_cursor aws_s3_upload_request_checksum_context_get_checksum_cursor(
144-
const struct aws_s3_upload_request_checksum_context *context);
151+
struct aws_s3_upload_request_checksum_context *context);
145152

146153
AWS_EXTERN_C_END
147154

include/aws/s3/private/s3_checksums.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ struct aws_input_stream *aws_checksum_stream_new(
8585
AWS_S3_API
8686
int aws_checksum_stream_finalize_checksum(struct aws_input_stream *checksum_stream, struct aws_byte_buf *checksum_buf);
8787

88+
/**
89+
* Finalize the checksum has read so far to the checksum context.
90+
* Not thread safe.
91+
*/
92+
AWS_S3_API
93+
int aws_checksum_stream_finalize_checksum_context(
94+
struct aws_input_stream *checksum_stream,
95+
struct aws_s3_upload_request_checksum_context *checksum_context);
96+
8897
/**
8998
* TODO: properly support chunked encoding.
9099
* Creates a chunked encoding stream that wraps an existing stream and adds checksum trailers.

source/s3_auto_ranged_put.c

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,15 +1099,20 @@ static void s_s3_prepare_upload_part_on_read_done(void *user_data) {
10991099
goto on_done;
11001100
}
11011101
struct aws_s3_upload_request_checksum_context *context = previously_uploaded_info->checksum_context;
1102-
/* if previously uploaded part had a checksum, compare it to what we just skipped */
1103-
if (context != NULL && context->checksum_calculated == true &&
1104-
s_verify_part_matches_checksum(
1105-
meta_request->allocator,
1106-
aws_byte_cursor_from_buf(&request->request_body),
1107-
meta_request->checksum_config.checksum_algorithm,
1108-
aws_byte_cursor_from_buf(&context->base64_checksum))) {
1109-
error_code = aws_last_error_or_unknown();
1110-
goto on_done;
1102+
if (context) {
1103+
if (!aws_s3_upload_request_checksum_context_should_calculate(context)) {
1104+
struct aws_byte_cursor previous_calculated_checksum =
1105+
aws_s3_upload_request_checksum_context_get_checksum_cursor(context);
1106+
/* if previously uploaded part had a checksum, compare it to what we just skipped */
1107+
if (s_verify_part_matches_checksum(
1108+
meta_request->allocator,
1109+
aws_byte_cursor_from_buf(&request->request_body),
1110+
meta_request->checksum_config.checksum_algorithm,
1111+
previous_calculated_checksum) != AWS_OP_SUCCESS) {
1112+
error_code = aws_last_error_or_unknown();
1113+
goto on_done;
1114+
}
1115+
}
11111116
}
11121117
}
11131118

@@ -1157,9 +1162,6 @@ static void s_s3_prepare_upload_part_finish(struct aws_s3_prepare_upload_part_jo
11571162
checksum_context = part->checksum_context;
11581163
/* If checksum already calculated, it means either the part being retried or the part resumed from list
11591164
* parts. Keep reusing the old checksum in case of the request body in memory mangled */
1160-
AWS_ASSERT(
1161-
!checksum_context->checksum_calculated || request->num_times_prepared > 0 ||
1162-
auto_ranged_put->resume_token != NULL);
11631165
aws_s3_meta_request_unlock_synced_data(meta_request);
11641166
}
11651167
/* END CRITICAL SECTION */

source/s3_checksum_context.c

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@
88
#include <aws/common/encoding.h>
99
#include <aws/common/logging.h>
1010

11+
static void s_lock_synced_data(struct aws_s3_upload_request_checksum_context *context) {
12+
aws_mutex_lock(&context->synced_data.lock);
13+
}
14+
15+
static void s_unlock_synced_data(struct aws_s3_upload_request_checksum_context *context) {
16+
aws_mutex_unlock(&context->synced_data.lock);
17+
}
18+
1119
static void s_aws_s3_upload_request_checksum_context_destroy(void *context) {
1220
struct aws_s3_upload_request_checksum_context *checksum_context = context;
13-
aws_byte_buf_clean_up(&checksum_context->base64_checksum);
21+
aws_byte_buf_clean_up(&checksum_context->synced_data.base64_checksum);
22+
aws_mutex_clean_up(&checksum_context->synced_data.lock);
1423
aws_mem_release(checksum_context->allocator, checksum_context);
1524
}
1625

@@ -44,6 +53,10 @@ static struct aws_s3_upload_request_checksum_context *s_s3_upload_request_checks
4453
return NULL;
4554
}
4655
context->encoded_checksum_size = encoded_size;
56+
if (!aws_mutex_init(&context->synced_data.lock)) {
57+
aws_s3_upload_request_checksum_context_release(context);
58+
return NULL;
59+
}
4760
return context;
4861
}
4962

@@ -54,7 +67,7 @@ struct aws_s3_upload_request_checksum_context *aws_s3_upload_request_checksum_co
5467
s_s3_upload_request_checksum_context_new_base(allocator, checksum_config);
5568
if (context && context->encoded_checksum_size > 0) {
5669
/* Initial the buffer for checksum */
57-
aws_byte_buf_init(&context->base64_checksum, allocator, context->encoded_checksum_size);
70+
aws_byte_buf_init(&context->synced_data.base64_checksum, allocator, context->encoded_checksum_size);
5871
}
5972
return context;
6073
}
@@ -79,8 +92,8 @@ struct aws_s3_upload_request_checksum_context *aws_s3_upload_request_checksum_co
7992
aws_s3_upload_request_checksum_context_release(context);
8093
return NULL;
8194
}
82-
aws_byte_buf_init_copy_from_cursor(&context->base64_checksum, allocator, existing_base64_checksum);
83-
context->checksum_calculated = true;
95+
aws_byte_buf_init_copy_from_cursor(&context->synced_data.base64_checksum, allocator, existing_base64_checksum);
96+
context->synced_data.checksum_calculated = true;
8497
}
8598
return context;
8699
}
@@ -101,14 +114,18 @@ struct aws_s3_upload_request_checksum_context *aws_s3_upload_request_checksum_co
101114
return NULL;
102115
}
103116

104-
bool aws_s3_upload_request_checksum_context_should_calculate(
105-
const struct aws_s3_upload_request_checksum_context *context) {
117+
bool aws_s3_upload_request_checksum_context_should_calculate(struct aws_s3_upload_request_checksum_context *context) {
106118
if (!context || context->algorithm == AWS_SCA_NONE) {
107119
return false;
108120
}
109121

122+
bool should_calculate = false;
123+
s_lock_synced_data(context);
110124
/* If not previous calculated */
111-
return !context->checksum_calculated;
125+
should_calculate = !context->synced_data.checksum_calculated;
126+
s_unlock_synced_data(context);
127+
128+
return should_calculate;
112129
}
113130

114131
bool aws_s3_upload_request_checksum_context_should_add_header(
@@ -129,19 +146,46 @@ bool aws_s3_upload_request_checksum_context_should_add_trailer(
129146
return context->location == AWS_SCL_TRAILER && context->algorithm != AWS_SCA_NONE;
130147
}
131148

132-
struct aws_byte_buf *aws_s3_upload_request_checksum_context_get_output_buffer(
133-
struct aws_s3_upload_request_checksum_context *context) {
149+
int aws_s3_upload_request_checksum_context_finalize_checksum(
150+
struct aws_s3_upload_request_checksum_context *context,
151+
struct aws_byte_cursor raw_checksum_cursor) {
134152
if (!context) {
135-
return NULL;
153+
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
136154
}
137-
return &context->base64_checksum;
155+
s_lock_synced_data(context);
156+
/* If not previous calculated */
157+
if (!context->synced_data.checksum_calculated) {
158+
AWS_ASSERT(context->synced_data.base64_checksum.len == 0);
159+
160+
if (aws_base64_encode(&raw_checksum_cursor, &context->synced_data.base64_checksum)) {
161+
aws_byte_buf_reset(&context->synced_data.base64_checksum, false);
162+
AWS_LOGF_ERROR(
163+
AWS_LS_S3_GENERAL,
164+
"Failed to base64 encode for the checksum. Raw checksum length: %zu. Output buffer capacity: %zu "
165+
"length %zu",
166+
raw_checksum_cursor.len,
167+
context->synced_data.base64_checksum.capacity,
168+
context->synced_data.base64_checksum.len);
169+
s_unlock_synced_data(context);
170+
return AWS_OP_ERR;
171+
}
172+
context->synced_data.checksum_calculated = true;
173+
}
174+
s_unlock_synced_data(context);
175+
return AWS_OP_SUCCESS;
138176
}
139177

140178
struct aws_byte_cursor aws_s3_upload_request_checksum_context_get_checksum_cursor(
141-
const struct aws_s3_upload_request_checksum_context *context) {
179+
struct aws_s3_upload_request_checksum_context *context) {
142180
struct aws_byte_cursor checksum_cursor = {0};
143-
if (!context || !context->checksum_calculated) {
181+
if (!context) {
144182
return checksum_cursor;
145183
}
146-
return aws_byte_cursor_from_buf(&context->base64_checksum);
184+
s_lock_synced_data(context);
185+
/* If not previous calculated */
186+
if (context->synced_data.checksum_calculated) {
187+
checksum_cursor = aws_byte_cursor_from_buf(&context->synced_data.base64_checksum);
188+
}
189+
s_unlock_synced_data(context);
190+
return checksum_cursor;
147191
}

source/s3_checksum_stream.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0.
44
*/
55

6+
#include "aws/s3/private/s3_checksum_context.h"
67
#include "aws/s3/private/s3_checksums.h"
78
#include <aws/common/encoding.h>
89
#include <aws/io/stream.h>
@@ -135,3 +136,31 @@ int aws_checksum_stream_finalize_checksum(struct aws_input_stream *checksum_stre
135136

136137
return AWS_OP_SUCCESS;
137138
}
139+
140+
int aws_checksum_stream_finalize_checksum_context(
141+
struct aws_input_stream *checksum_stream,
142+
struct aws_s3_upload_request_checksum_context *checksum_context) {
143+
struct aws_checksum_stream *impl = AWS_CONTAINER_OF(checksum_stream, struct aws_checksum_stream, base);
144+
145+
if (aws_checksum_finalize(impl->checksum, &impl->checksum_result) != AWS_OP_SUCCESS) {
146+
AWS_LOGF_ERROR(
147+
AWS_LS_S3_CLIENT,
148+
"Failed to calculate checksum with error code %d (%s).",
149+
aws_last_error(),
150+
aws_error_str(aws_last_error()));
151+
aws_byte_buf_reset(&impl->checksum_result, true);
152+
return aws_raise_error(AWS_ERROR_S3_CHECKSUM_CALCULATION_FAILED);
153+
}
154+
struct aws_byte_cursor checksum_result_cursor = aws_byte_cursor_from_buf(&impl->checksum_result);
155+
if (aws_s3_upload_request_checksum_context_finalize_checksum(checksum_context, checksum_result_cursor) !=
156+
AWS_OP_SUCCESS) {
157+
AWS_LOGF_ERROR(
158+
AWS_LS_S3_CLIENT,
159+
"Failed to finalize checksum context with error code %d (%s).",
160+
aws_last_error(),
161+
aws_error_str(aws_last_error()));
162+
aws_byte_buf_reset(&impl->checksum_result, true);
163+
return aws_raise_error(AWS_ERROR_S3_CHECKSUM_CALCULATION_FAILED);
164+
}
165+
return AWS_OP_SUCCESS;
166+
}

source/s3_chunk_stream.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,15 @@ static int s_set_post_chunk_stream(struct aws_chunk_stream *parent_stream) {
6262
}
6363
struct aws_byte_cursor post_trailer_cursor = aws_byte_cursor_from_string(s_post_trailer);
6464
struct aws_byte_cursor colon_cursor = aws_byte_cursor_from_string(s_colon);
65-
/* After the checksum stream released, the checksum will be calculated. */
66-
if (parent_stream->checksum_context->checksum_calculated == false) {
67-
AWS_ASSERT(parent_stream->checksum_context->base64_checksum.len == 0);
68-
AWS_ASSERT(parent_stream->checksum_stream != NULL);
69-
/* Get the checksum from the checksum stream of the data read from it. */
70-
if (aws_checksum_stream_finalize_checksum(
71-
parent_stream->checksum_stream, &parent_stream->checksum_context->base64_checksum)) {
65+
if (parent_stream->checksum_stream) {
66+
/* If we have the checksum stream, finalize the checksum now as we finished reading from it. */
67+
if (aws_checksum_stream_finalize_checksum_context(
68+
parent_stream->checksum_stream, parent_stream->checksum_context)) {
7269
return AWS_OP_ERR;
7370
}
74-
parent_stream->checksum_context->checksum_calculated = true;
7571
}
7672
struct aws_byte_cursor checksum_result_cursor =
77-
aws_byte_cursor_from_buf(&parent_stream->checksum_context->base64_checksum);
73+
aws_s3_upload_request_checksum_context_get_checksum_cursor(&parent_stream->checksum_context);
7874
AWS_ASSERT(parent_stream->checksum_context->encoded_checksum_size == checksum_result_cursor.len);
7975

8076
if (aws_byte_buf_init(
@@ -175,7 +171,6 @@ static void s_aws_input_chunk_stream_destroy(struct aws_chunk_stream *impl) {
175171
aws_byte_buf_clean_up(&impl->pre_chunk_buffer);
176172
aws_byte_buf_clean_up(&impl->post_chunk_buffer);
177173
/* Either we calculated the checksum, or we the checksum is empty. Otherwise, something was wrong. */
178-
AWS_ASSERT(impl->checksum_context->checksum_calculated || impl->checksum_context->base64_checksum.len == 0);
179174
aws_s3_upload_request_checksum_context_release(impl->checksum_context);
180175
aws_mem_release(impl->allocator, impl);
181176
}
@@ -207,7 +202,7 @@ struct aws_input_stream *aws_chunk_stream_new(
207202
impl->checksum_context = aws_s3_upload_request_checksum_context_acquire(checksum_context);
208203

209204
algorithm = checksum_context->algorithm;
210-
bool checksum_calculated = checksum_context->checksum_calculated;
205+
bool should_calculate_checksum = aws_s3_upload_request_checksum_context_should_calculate(impl->checksum_context);
211206

212207
int64_t stream_length = 0;
213208
int64_t final_chunk_len = 0;
@@ -230,7 +225,7 @@ struct aws_input_stream *aws_chunk_stream_new(
230225
if (aws_byte_buf_append(&impl->pre_chunk_buffer, &pre_chunk_cursor)) {
231226
goto error;
232227
}
233-
if (!checksum_calculated) {
228+
if (should_calculate_checksum) {
234229
/* Wrap the existing stream with checksum stream to calculate the checksum when reading from it. */
235230
impl->checksum_stream = aws_checksum_stream_new(allocator, existing_stream, algorithm);
236231
if (impl->checksum_stream == NULL) {

source/s3_request_messages.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -838,13 +838,9 @@ static int s_calculate_in_memory_checksum_helper(
838838
struct aws_byte_cursor data,
839839
struct aws_s3_upload_request_checksum_context *checksum_context) {
840840
AWS_ASSERT(checksum_context);
841-
if (checksum_context->checksum_calculated) {
842-
return AWS_OP_SUCCESS;
843-
}
844841

845842
int ret_code = AWS_OP_ERR;
846843
/* Calculate checksum for output buffer only (no header/trailer) */
847-
struct aws_byte_buf *output_buffer = aws_s3_upload_request_checksum_context_get_output_buffer(checksum_context);
848844
struct aws_byte_buf raw_checksum;
849845
size_t digest_size = aws_get_digest_size_from_checksum_algorithm(checksum_context->algorithm);
850846
aws_byte_buf_init(&raw_checksum, allocator, digest_size);
@@ -855,18 +851,14 @@ static int s_calculate_in_memory_checksum_helper(
855851
}
856852

857853
struct aws_byte_cursor raw_checksum_cursor = aws_byte_cursor_from_buf(&raw_checksum);
858-
if (aws_base64_encode(&raw_checksum_cursor, output_buffer)) {
854+
if (aws_s3_upload_request_checksum_context_finalize_checksum(checksum_context, raw_checksum_cursor)) {
859855
aws_byte_buf_clean_up(&raw_checksum);
860856
goto done;
861857
}
862858
aws_byte_buf_clean_up(&raw_checksum);
863-
checksum_context->checksum_calculated = true;
864859

865860
ret_code = AWS_OP_SUCCESS;
866861
done:
867-
if (ret_code) {
868-
aws_byte_buf_clean_up(output_buffer);
869-
}
870862
aws_byte_buf_clean_up(&raw_checksum);
871863
return ret_code;
872864
}
@@ -890,7 +882,8 @@ static int s_calculate_and_add_checksum_to_header_helper(
890882
/* Add the encoded checksum to header. */
891883
const struct aws_byte_cursor header_name =
892884
aws_get_http_header_name_from_checksum_algorithm(checksum_context->algorithm);
893-
struct aws_byte_cursor encoded_checksum_val = aws_byte_cursor_from_buf(&checksum_context->base64_checksum);
885+
struct aws_byte_cursor encoded_checksum_val =
886+
aws_s3_upload_request_checksum_context_get_checksum_cursor(checksum_context);
894887
struct aws_http_headers *headers = aws_http_message_get_headers(out_message);
895888
if (aws_http_headers_set(headers, header_name, encoded_checksum_val)) {
896889
goto done;

tests/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,6 @@ add_test_case(crc32c_test_invalid_buffer)
294294
add_test_case(crc32c_test_oneshot)
295295
add_test_case(crc32c_test_invalid_state)
296296

297-
add_test_case(test_upload_request_checksum_context_get_output_buffer)
298297
add_test_case(test_upload_request_checksum_context_get_checksum_cursor)
299298
add_test_case(test_upload_request_checksum_context_error_cases)
300299
add_test_case(test_upload_request_checksum_context_different_algorithms)

0 commit comments

Comments
 (0)