Skip to content

Commit ac7161c

Browse files
Fix concurrency issue in mutlithreaded environment (2.rc.03)
Problem: If two threads called base64_encode concurrently (one with url=true and the other with url=false, the values of base64_chars[62] and base64_chars[63] are undefined. For example, it's possible to get base64_chars[62]='-' and base64_chars[63]='/', which is not a valid encoding. This commit fixes this issue.
1 parent 2ca8b88 commit ac7161c

File tree

3 files changed

+79
-24
lines changed

3 files changed

+79
-24
lines changed

base64.cpp

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
More information at
66
https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
77
8-
Version: 2.rc.02 (release candidate)
8+
Version: 2.rc.03 (release candidate)
99
1010
Copyright (C) 2004-2017, 2020 René Nyffenegger
1111
@@ -33,16 +33,25 @@
3333

3434
#include "base64.h"
3535

36-
static std::string base64_chars =
36+
//
37+
// Depending on the url parameter in base64_chars, one of
38+
// two sets of base64 characters needs to be chosen.
39+
// They differ in their last two characters.
40+
//
41+
const char* base64_chars[2] = {
3742
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3843
"abcdefghijklmnopqrstuvwxyz"
3944
"0123456789"
40-
"??"; // These two question marks will be replaced based on the value of url in base64_encode
45+
"+/",
4146

47+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
48+
"abcdefghijklmnopqrstuvwxyz"
49+
"0123456789"
50+
"-_"};
4251

4352
static std::size_t pos_of_char(const unsigned char chr) {
4453
//
45-
// Return the position of chr within base64_chars.
54+
// Return the position of chr within base64_encode()
4655
//
4756

4857
if (chr >= 'A' && chr <= 'Z') return chr - 'A';
@@ -93,45 +102,45 @@ static std::string encode(String s, bool url) {
93102
}
94103

95104
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len, bool url) {
96-
//
97-
// Replace question marks in base64_chars:
98-
//
99-
if (url) {
100-
base64_chars[62] = '-';
101-
base64_chars[63] = '_';
102-
}
103-
else {
104-
base64_chars[62] = '+';
105-
base64_chars[63] = '/';
106-
}
107105

108106
unsigned int len_encoded = (in_len +2) / 3 * 4;
109107

110108
unsigned char trailing_char = url ? '.' : '=';
111109

110+
//
111+
// Choose set of base64 characters. They differ
112+
// for the last two positions, depending on the url
113+
// parameter.
114+
// A bool (as is the parameter url) is guaranteed
115+
// to evaluate to either 0 or 1 in C++ therfore,
116+
// the correct character set is chosen by subscripting
117+
// base64_chars with url.
118+
//
119+
const char* base64_chars_ = base64_chars[url];
120+
112121
std::string ret;
113122
ret.reserve(len_encoded);
114123

115124
unsigned int pos = 0;
116125

117126
while (pos < in_len) {
118-
ret.push_back(base64_chars[(bytes_to_encode[pos + 0] & 0xfc) >> 2]);
127+
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]);
119128

120129
if (pos+1 < in_len) {
121-
ret.push_back(base64_chars[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]);
130+
ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]);
122131

123132
if (pos+2 < in_len) {
124-
ret.push_back(base64_chars[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]);
125-
ret.push_back(base64_chars[ bytes_to_encode[pos + 2] & 0x3f]);
133+
ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]);
134+
ret.push_back(base64_chars_[ bytes_to_encode[pos + 2] & 0x3f]);
126135
}
127136
else {
128-
ret.push_back(base64_chars[(bytes_to_encode[pos + 1] & 0x0f) << 2]);
137+
ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]);
129138
ret.push_back(trailing_char);
130139
}
131140
}
132141
else {
133142

134-
ret.push_back(base64_chars[(bytes_to_encode[pos + 0] & 0x03) << 4]);
143+
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]);
135144
ret.push_back(trailing_char);
136145
ret.push_back(trailing_char);
137146
}

base64.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// base64 encoding and decoding with C++.
3-
// Version: 2.rc.02 (release candidate)
3+
// Version: 2.rc.03 (release candidate)
44
//
55

66
#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A

test.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,58 @@ int main() {
9696
// test checks if this bug was fixed:
9797
//
9898
std::string a17_orig = "aaaaaaaaaaaaaaaaa";
99-
std::string a17_encoded = base64_encode(a17_orig, true);
100-
if (base64_decode(a17_encoded) != a17_orig) {
99+
std::string a17_encoded = base64_encode(a17_orig, false);
100+
std::string a17_encoded_url = base64_encode(a17_orig, true );
101+
102+
if (a17_encoded != "YWFhYWFhYWFhYWFhYWFhYWE=") {
101103
std::cout << "Failed to encode a17" << std::endl;
102104
all_tests_passed = false;
103105
}
104106

107+
if (a17_encoded_url != "YWFhYWFhYWFhYWFhYWFhYWE.") {
108+
std::cout << "Failed to encode a17 (url)" << std::endl;
109+
all_tests_passed = false;
110+
}
111+
112+
if (base64_decode(a17_encoded_url) != a17_orig) {
113+
std::cout << "Failed to decode a17 (url)" << std::endl;
114+
all_tests_passed = false;
115+
}
116+
117+
if (base64_decode(a17_encoded ) != a17_orig) {
118+
std::cout << "Failed to decode a17 (no url)" << std::endl;
119+
all_tests_passed = false;
120+
}
121+
122+
// --------------------------------------------------------------
123+
124+
// characters 63 and 64 / URL encoding
125+
126+
std::string s_6364 = "\x03" "\xef" "\xff" "\xf9";
127+
128+
std::string s_6364_encoded = base64_encode(s_6364, false);
129+
std::string s_6364_encoded_url = base64_encode(s_6364, true );
130+
131+
if (s_6364_encoded != "A+//+Q==") {
132+
std::cout << "Failed to encode_6364 (no url)" << std::endl;
133+
all_tests_passed = false;
134+
}
135+
136+
if (s_6364_encoded_url!= "A-__-Q..") {
137+
std::cout << "Failed to encode_6364 (url)" << std::endl;
138+
all_tests_passed = false;
139+
}
140+
141+
if (base64_decode(s_6364_encoded ) != s_6364) {
142+
std::cout << "Failed to decode s_6364_encoded" << std::endl;
143+
all_tests_passed = false;
144+
}
145+
146+
if (base64_decode(s_6364_encoded_url) != s_6364) {
147+
std::cout << "Failed to decode s_6364_encoded_url" << std::endl;
148+
all_tests_passed = false;
149+
}
150+
105151

106152
// --------------------------------------------------------------
107153

0 commit comments

Comments
 (0)