Strange behavior (anonymous namespace with swscanf) - c++

I have the next experimental code. That code fail with segmentation fault on the labeled line.
If I take out the str2MxfKey function from anonymous namespace or give a some name to namespace, code will run without any problem.
That strange behavior reproduced only on the release build. Could someone explain me root cause of that problem?
Ubuntu 22.04, cmake 3.22.1, gcc 11.3.0
struct mxfKey
{
uint8_t octet0;
uint8_t octet1;
uint8_t octet2;
uint8_t octet3;
uint8_t octet4;
uint8_t octet5;
uint8_t octet6;
uint8_t octet7;
uint8_t octet8;
uint8_t octet9;
uint8_t octet10;
uint8_t octet11;
uint8_t octet12;
uint8_t octet13;
uint8_t octet14;
uint8_t octet15;
};
namespace {
mxfKey str2MxfKey(const std::wstring &str) {
mxfKey k;
int rc = swscanf(str.c_str(),
L"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", // mxfkey
&k.octet0, &k.octet1, &k.octet2, &k.octet3,
&k.octet4, &k.octet5, &k.octet6, &k.octet7,
&k.octet8, &k.octet9, &k.octet10, &k.octet11,
&k.octet12, &k.octet13, &k.octet14, &k.octet15);
if (rc != 16) {
throw std::runtime_error("Error in str2MxfKey");
}
return k;
}
}
void someExperiments() {
std::wstring wstr = L"8c2197ad00e9476b8213b367123e506e";
std::wcout << "Start" << std::endl;
str2MxfKey(wstr);
std::wcout << wstr << std::endl; // <---- Segmentation fault (core dumped)
std::wcout << "End" << std::endl;
}
int main(int argc, char* argv[]) {
someExperiments();
...
}

The swscanf format string is wrong. %02x expects an unsigned int and not uint8_t.
Try this instead:
int rc = swscanf(str.c_str(),
L"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", // mxfkey
&k.octet0, &k.octet1, &k.octet2, &k.octet3,
&k.octet4, &k.octet5, &k.octet6, &k.octet7,
&k.octet8, &k.octet9, &k.octet10, &k.octet11,
&k.octet12, &k.octet13, &k.octet14, &k.octet15);

Related

Different hash values for identical input using different OpenSSL evp.h functions

Using the following code, why do I get different hash values?
#include <iostream>
#include <cstring>
#include <openssl/evp.h>
# define SHA256_DIGEST_LENGTH 32
using namespace std;
static void hex_print(const char *name, const unsigned char *buf, size_t len)
{
size_t i;
fprintf(stdout, "%s ", name);
for (i = 0; i < len; i++)
fprintf(stdout, "%02X", buf[i]);
fputs("\n", stdout);
}
int main (int argc, char *argv[])
{
EVP_MD_CTX *ctx;
//const EVP_MD *evp_md = EVP_sha256();
char *msg = (char*)"I have the high ground...";
unsigned int msg_size = strlen(msg);
unsigned char digest_a[SHA256_DIGEST_LENGTH] = {0};
unsigned char digest_b[SHA256_DIGEST_LENGTH] = {0};
cout << "message: " << msg << endl;
cout << "length: " << msg_size << endl;
ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
EVP_DigestUpdate(ctx, msg, msg_size);
EVP_DigestFinal_ex(ctx, digest_a, &msg_size);
hex_print("sha256: ", digest_a, SHA256_DIGEST_LENGTH);
EVP_Digest (msg, msg_size, digest_b, NULL, EVP_sha256 (), NULL);
hex_print ("sha256: ", digest_b, SHA256_DIGEST_LENGTH);
EVP_MD_CTX_free(ctx);
return EXIT_SUCCESS;
}
Compiled on Ubuntu 20.04 LTS using gcc
g++ -Wall -g -o test test.cpp -I/usr/include/openssl -L/usr/lib/x86_64-linux-gnu l:libcrypto.so
Output:
message: I have the high ground...
length: 25
sha256: 2873FC569EFBE61AC67A1247CCBAE0AE94BE97D227D1ECEF3C9E93A8EA3CAD33
sha256: F13852B3A521A6D08169AAA42525AA63FEF87D8A22418A25A8E19F3FD15E750D
When I paste the string into an online tool such as https://emn178.github.io/online-tools/sha256.html, the "correct" hash seems to be the first one:
2873fc569efbe61ac67a1247ccbae0ae94be97d227d1ecef3c9e93a8ea3cad33
Can anyone explain me why this is happening? Thanks!
The call EVP_DigestFinal_ex(ctx, digest_a, &msg_size) overwrites msg_size to SHA256_DIGEST_LENGTH, which is not equal to the original 25.
You then go on to use msg_size as if it was still the original value.

Segmentation Fault on dereferencing mapped memory

I'm running into an annoying issue where I cannot access memory obtained from mmap in any way without getting a segmentation fault.
The function I used to obtain the mapped memory looks like this.
/**
* Preconditions: filename must be verified as referencing a valid file.
*/
char *IOUtils::memory_map_file(string const& filename, size_t length, int open_flags){
int fd = open(filename.c_str(), open_flags | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int prot;
if (open_flags == O_RDONLY)
prot = PROT_READ;
else
prot = PROT_READ | PROT_WRITE;
void *output = mmap(NULL, length, prot, MAP_SHARED, fd, 0);
if (output == (void *) -1){
cerr << filename << ": " << strerror(errno) << '\n';
_exit(2);
}
close(fd);
return (char *)output;
}
My main function looks like this.
int main(int argc, char *argv[]){
size_t input_length = IOUtils::file_size(argv[1]); //This works fine
char *input_buffer = IOUtils::memory_map_file(argv[1], input_length,
O_RDONLY); //This succeeds
char *output_buffer = IOUtils::memory_map_file(argv[2], 2*input_length,
O_RDWR); //This succeeds
DomainParser parser(input_length, input_buffer, output_buffer);
while(!parser.finished()){
parser.write_entry();
}
mremap(output_buffer, 2*input_length, MREMAP_MAYMOVE,
parser.bytes_written());
munmap(output_buffer, parser.bytes_written());
}
The parser's relevant code looks like this
void DomainParser::write_entry(void){
char const *in = input(); //Gets position in input file
char const *copy_up_to = end(); //Gets position at input EOF
for(char const *it = in; it < copy_up_to; ++it){
cerr << *it; //SIGSEGV!
if(*it == '\n') break;
}
cerr << '\n';
/* Do writes */
}
The program segfaults immediately upon cerr << *it. I have no idea why this would happen, considering all mapped memory is equipped with read permissions and is successfully allocated.
Edit: If anyone suspects the class is broken somewhere, here's the full source code.
using std::stringstream;
using std::string;
class DomainParser{
size_t _input_offset;
const size_t _input_length;
size_t _output_offset;
char const *input_buffer;
char *output_buffer;
char const *input(void){
return input_buffer + _input_offset;
}
char *output(void){
return output_buffer + _output_offset;
}
char const* end(void){
return input_buffer + _input_length;
}
char const *find(char const *begin, char const *max, char c){
while (*begin != c){
cerr << *begin++;
}
cerr << c;
return begin;
}
public:
DomainParser(size_t length, char const *input, char *output) :
_input_length(length), input_buffer(input), output_buffer(output)
{}
bool finished(void){
return _input_offset == _input_length;
}
size_t bytes_written(void){
return _output_offset;
}
size_t write_entry(void){
if (finished()){
return 0;
}
char const *in = input();
char const *copy_up_to = find(in, end(), '\n');
size_t input_entry_length = copy_up_to - in;
string s(in, copy_up_to);
stringstream ss(s);
string name, type, host;
ss >> name >> type >> host;
if (!ss){
cerr << s << '\n';
_input_offset += input_entry_length;
return 0;
}
ss.str(""); ss.clear();
ss << "{\"name\":\"" << name << "\"," <<
"\"host\":\"" << host << "\"," <<
"\"type\":\"" << type << "\"}\n";
string entry = ss.str();
std::memcpy(output(), entry.c_str(), entry.size());
_input_offset += input_entry_length;
_output_offset += entry.size();
return entry.size();
}
};
I don't see any initialization of _input_offset.
If you fix that, you will run into the problem that the output file is empty, so accessing any pages will trigger a SIGBUS signal. You need to resize it using ftruncate to the intended size (probably to match the size of the mapping, but this depends on what you are trying to do).
Also not that munmap can be very expensive (especially on large systems), so memory-mapped I/O is only a win when the file sizes are quite large.

C++ / Gcc - Stack smashing when a func returns a struct

I'm actually having troubles with a simple program which is supposed to pass a struct through named pipes.
Here is my main.cpp:
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include "NamedPipe.hh"
int main()
{
pid_t pid;
std::string str("test_namedPipe");
NamedPipe pipe(str);
message *msg;
//Initialisation of my struct
msg = (message *)malloc(sizeof(message) + sizeof(char) * 12);
msg->type = 1;
sprintf(msg->str, "Hello World");
//Forking
pid = fork();
if (pid != 0) {
pipe.send(msg);
} else {
message msg_receive = pipe.receive(); //Here is the overflow
std::cout << "type: " << msg_receive.type << " file: " << msg_receive.str << std::endl;
}
return (0);
}
My NamedPipe.cpp:
#include "NamedPipe.hh"
#include <stdio.h>
NamedPipe::NamedPipe(std::string const &_name) : name("/tmp/" + _name) {
mkfifo(name.c_str(), 0666);
// std::cout << "create fifo " << name << std::endl;
}
NamedPipe::~NamedPipe() {
unlink(name.c_str());
}
void NamedPipe::send(message *msg) {
int fd;
int size = sizeof(char) * 12 + sizeof(message);
fd = open(name.c_str(), O_WRONLY);
write(fd, &size, sizeof(int));
write(fd, msg, (size_t)size);
close(fd);
}
message NamedPipe::receive() {
int fd;
int size;
message msg;
fd = open(name.c_str(), O_RDONLY);
read(fd, &size, sizeof(int));
read(fd, &msg, (size_t)size);
close(fd);
return (msg); //I debugged with printf. This actually reach this point before overflow
}
And my struct is defined like:
struct message {
int type;
char str[0];
};
I actually think that may be a problem of memory allocation, but I have really no idea of what I should do to fix this.
Thanks for reading/helping !
This is the root of your problem, your struct message:
char str[0];
This is not kosher in C++ (nor is the way you're using it kosher in C). When you allocate a message on the stack, you're allocating room for one int and 0 chars. Then in this line
read(fd, &msg, (size_t)size);
you write beyond your stack allocation into neverland. Then you return your message object which would be just one int in size.
Change your struct to this, and it should "work"
struct message
{
int type;
char str[ 16 ];
};

basic_streambuf does not set eof bit when using setp

I'm trying to use a std::ostream backed by a buffer that I own.
When using setp to have the std::basic_streambuf to us my buffer, everything works fine expect that I would have expected the eof to be set when writing more bytes that were allocated for my buffer. The fail and bad bits are being set while the eof bit is not.
Is this the expected behavior?
If not, how can I get the eof bit to be set?
#include <iostream>
#include <streambuf>
template <typename char_type>
struct OStreamBufWrapper : public std::basic_streambuf<char_type, std::char_traits<char_type> >
{
OStreamBufWrapper(char_type* buffer, std::streamsize bufferLength)
{
// set the "put" pointer the start of the buffer and record its length.
this->setp(buffer, buffer + bufferLength);
}
};
int main(int argc, const char * argv[])
{
int bufsize = 10;
char *buf = new char[bufsize];
OStreamBufWrapper<char> ost(buf, bufsize);
std::ostream sb(&ost);
char i = 0;
while ( 1 )
{
sb.write(&i, 1);
if (!sb.good())
{
if (sb.fail())
std::cout << "fail\n";
if (sb.bad())
std::cout << "bad\n";
if (sb.eof())
std::cout << "eof\n";
break;
}
i++;
}
delete [] buf;
return 0;
}

openssl hash producing partially different output - c++ ubuntu

Below is my code for creating a hash in openssl.
I was wondering why is my output always partially diffrent.
int main()
{
char alg[] = "MD5";
char buf[EVP_MAX_KEY_LENGTH] = "5";
unsigned char *testKey;
unsigned int olen;
testKey = simple_digest(alg, buf, EVP_MAX_KEY_LENGTH,&olen);
std::cout << "Printing key : >>";
print_hex(testKey,EVP_MAX_KEY_LENGTH);
std::cout << "<<" << std::endl;
}
unsigned char *simple_digest(char *alg, char *buf, unsigned int len,unsigned int *olen)
{
const EVP_MD *m;
EVP_MD_CTX ctx;
unsigned char *ret;
OpenSSL_add_all_digests();
if (!(m = EVP_get_digestbyname(alg)))
return NULL;
if (!(ret = (unsigned char *)malloc(EVP_MAX_MD_SIZE)))
return NULL;
EVP_DigestInit(&ctx, m);
EVP_DigestUpdate(&ctx, buf, len);
EVP_DigestFinal(&ctx, ret, olen);
// std::cout << "computed key" << ret << std::endl;
return ret;
}
void print_hex(unsigned char *bs, unsigned int n)
{
int i;
for (i = 0; i < n; i++)
printf("%02x", bs[i]);
printf("\n");
}
Output :
3afb8ebc9c93bf6d40285736f210b7856af8bab4d040ca090043ca09f840ca09
3afb8ebc9c93bf6d40285736f210b7856af8bab490a5f909c0a7f909b8a5f909
As you can see only the back few characters differ.
Thanks in advance! :D
Update (correct working version) :
int main()
{
char alg[] = "MD5";
char buf[EVP_MAX_KEY_LENGTH] = "5";
unsigned char *testKey;
unsigned int olen;
testKey = simple_digest(alg, buf,strlen(buf),&olen);
std::cout << "Printing key : >>";
print_hex(testKey,olen);
std::cout << "<<" << std::endl;
}
unsigned char *simple_digest(char *alg, char *buf, unsigned int len,unsigned int *olen)
{
const EVP_MD *m;
EVP_MD_CTX ctx;
unsigned char *ret;
OpenSSL_add_all_digests();
if (!(m = EVP_get_digestbyname(alg)))
return NULL;
if (!(ret = (unsigned char *)malloc(EVP_MAX_KEY_LENGTH)))
return NULL;
EVP_DigestInit(&ctx, m);
EVP_DigestUpdate(&ctx, buf, len);
EVP_DigestFinal(&ctx, ret, olen);
return ret;
}
void print_hex(unsigned char *bs, unsigned int n)
{
int i;
for (i = 0; i < n; i++)
printf("%02x", bs[i]);
}
output :
Printing key : >>e4da3b7fbbce2345d7772b0674a318d5<<
You are trying to hash a one-character string, "5", but you are telling EVP_Digest_Update that your buffer length is EVP_MAX_KEY_LENGTH. You need to pass the actual length of your buffer, not the maximum length it could be.
For completeness, here is what your main function should look like:
int main()
{
char alg[] = "MD5";
char buf[EVP_MAX_KEY_LENGTH] = "5";
unsigned char *testKey;
unsigned int olen;
# Pass the true length of but
testKey = simple_digest(alg, buf, strlen(buf), &olen);
std::cout << "Printing key : >>";
# Pass the true length of testKey
print_hex(testKey,olen);
std::cout << "<<" << std::endl;
}
One other caveat: you probably don't want to use EVP_MAX_KEY_LENGTH for your buffer, as that would limit the size of the message you could hash.