How did this memory corruption occur in c++? - c++

I'm not sure I understand what the bug below is
const char* packs[] = {"zero","one","two","three","four",..."twelve"} //abbreviated for post
struct packinfo {
char* data;
int len;
};
std::vector<packinfo> k;
k.reserve(10000);
for (int i = 0; i < 10; ++i) {
const char* data = packs[i];
packinfo tobuf;
tobuf.data = new char[strlen(data)];
tobuf.len = strlen(data);
memcpy(tobuf.data, data, strlen(data));
k.push_back(tobuf);
}
for (int i = 0; i < k.size(); ++i)
std::cout << "k[" << i << "]: " << k[i].data << ", ";
std::cout << std::endl;
for (int i = 0; i < k.size(); ++i) {
packinfo& pack = k[i];
bool foo = (i < 5);
if (foo) std::cout << "inspecting k[" << i << "]: " << k[i].data << std::endl;
delete pack.data;
if (!foo) {
k.erase(k.begin(), k.begin() + i);
packinfo tobuf;
const char* data = packs[10];
tobuf.data = new char[strlen(data)];
tobuf.len = strlen(data);
memcpy(tobuf.data, data, strlen(data));
break; //intentionally forgot to push_back
}
}
for (int i = 0; i < k.size(); ++i)
std::cout << "k[" << i << "]: " << k[i].data << ", ";
std::cout << std::endl
;
The output of running the above is the following:
k[0]: zero, k[1]: one, ... , k[9]: nine, //all as expected
inspecting k[0]: zero
inspecting k[1]: one
...
inspecting k[4]: four
k[0]: ten^], k[1]: six, k[2] seven, k[3]: eight, k[4]L nine, //gargabe crept in
How did garbage creep into the beginning of the vector?

strlen gives you the length of characters in a nul-terminated string wihtout counting the nul-termination character. So you are dynamically allocating a data buffer that is too short to hold the target string:
tobuf.data = new char[strlen(data)]; // too short by 1
When you fill it using memcpy, there is no space for a null-termination for the string, and you wouldn't copy it if there was anyway, because the array is too short:
memcpy(tobuf.data, data, strlen(data)); // tobuf.data is not nul-terminated
When you attempt to read it as if it were a nul terminated string, you go out of bounds.
The immediate fix would be to use strlen(data) +1, but what you really should do is avoid the whole problem by replacing packinfo by std::string.
std::vector<std::string> k;
k.reserve(10000);

The problem is these lines:
tobuf.data = new char[strlen(data)];
tobuf.len = strlen(data);
memcpy(tobuf.data, data, strlen(data));
Where do you add space for the string terminator?
C++ have the std::string class, you should really use it as it will help you with these kind of problems.

Related

char tab is setting tab[0] to 0

I have this code:
#include <iostream>
#include <cstdlib>
#include <stdio.h>
int main()
{
std::string napis = "Inhibitor";
int length = napis.length();
char hex[256];
std::cout << "Original: " << napis << '\n';
for (int i = 0; i < length; i++)
{
char buffer[2];
itoa(napis[i], buffer, 16);
hex[2*i] = buffer[0];
hex[2*i+1] = buffer[1];
}
for (int i = 0; i < length * 2 + 1; i++)
{
std::cout << hex[i];
}
}
After all those operations I get that hex[0] == 0. Don't really know why. I am not setting this value anywhere I guess. Program is supposed to code string into hexadecimal.
The itoa function will write a terminating nul character into the buffer. In your code, this it will write 2 or 3 characters (depending in the ASCII value of the character in the string), which can exceed the array bounds.
The solution is to increase the size of buffer by at least 1:
char buffer[4];
Even better is to use the non-deprecated name (_itoa), which, in C++ with a character array, will call the safe version of the function (_itoa_s) which will detect buffer overruns.
Just avoid the temporary buffer and write directly in the result buffer.
char str[] {"abcdefg"};
char hex[256];
for (uint i {0}; i < sizeof(str) - 1; ++i) {
::sprintf (&hex[i*2], "%02X", str[i]);
}
std::cout << "Hex: " << hex << std::endl;

Why does creating 2 variables cause a crash in custom STL, C++ VS2019?

Hello I'm trying to rewrite my own memory manager and STL (nothing fancy, just some basic vector and string features) and I'm getting a strange behaviour. I'm trying to get experience in the memory management field because I'm a high school student with time to spare. The problem is, when I create my first variable everything goes perfectly but after creating the second variable, the program crashes while creating the first variable.
String.h/.cpp
class String {
char* pointer_toBuffer = nullptr;
size_t buffer_length = 0;
IAllocator* Allocator;
public:
String(const char* text, IAllocator* Allocator);}
String::String(const char* text, TuranAPI::MemoryManagement::IAllocator* MemoryAllocator) : Allocator(MemoryAllocator) {
std::cout << "String creation has started: " << text << std::endl;
unsigned int i = 0;
while (text[i] != 0) {
i++;
}
buffer_length = i + 1;
pointer_toBuffer = (char*)Allocator->Allocate_MemoryBlock(buffer_length * sizeof(char));//When I write the Second String part, FirstString crashes directly. I use VSDebug and it says access violation here while creating FirstString. It is successful if I delete the SecondString part.
for (unsigned int letterindex = 0; letterindex < i; letterindex++) {
pointer_toBuffer[letterindex] = text[letterindex];
}
pointer_toBuffer[i] = 0;
}
MemoryManagement.h/cpp
TAPIMemoryAllocator::TAPIMemoryAllocator(MemoryBlockInfo MemoryPool_toUse){
std::cout << "TAPIMemoryAllocator is created!\n";
std::cout << "MemoryPool's start pointer: " << MemoryPool_toUse.address << std::endl;
MemoryPool.address = MemoryPool_toUse.address;
MemoryPool.size = MemoryPool_toUse.size;
SELF = this;
}
void* TAPIMemoryAllocator::Allocate_MemoryBlock(size_t size) {
std::cout << "MemoryPool's start pointer: " << MemoryPool.address << std::endl;
std::cout << "A buffer of " << size << " bytes allocation request found in TAPIMemoryAllocator!\n";
if (SELF == nullptr) {
TMemoryManager First(1024 * 1024 * 1024 * 1);
MemoryBlockInfo FirstMemoryBlock;
FirstMemoryBlock.address = SELF->MemoryPool.address;
FirstMemoryBlock.size = size;
Allocated_MemoryBlocks[0] = FirstMemoryBlock;
return (char*)SELF->MemoryPool.address;
}
void* finaladdress = SELF->MemoryPool.address;
for (unsigned int blockindex = 0; blockindex < MAX_MEMORYBLOCKNUMBER; blockindex++) {
MemoryBlockInfo& MemoryBlock = Allocated_MemoryBlocks[blockindex];
finaladdress = (char*)finaladdress + MemoryBlock.size;
if (size <= MemoryBlock.size && MemoryBlock.address == nullptr) {
std::cout << "Intended block's size is less than found memory block!\n";
MemoryBlock.address = finaladdress;
//You shouldn't change Memory Block's size because all of the allocations before this are based upon the previous size!
//You should move all the previous allocated memory to set the size (which is not ideal!)
//If I'd want to find memory leaks causing this, I could write code here to log the leaks!
return MemoryBlock.address;
}
else if (MemoryBlock.size == 0 && MemoryBlock.address == nullptr) {
std::cout << "An empty block is created for intended block! Block's Array index is: " << blockindex << "\n";
std::cout << "MemoryPool's start pointer: " << MemoryPool.address << std::endl << "MemoryBlock's pointer: " << finaladdress << std::endl;
//This means this index in the Allocated_MemoryBlocks has never been used, so we can add the data here!
MemoryBlock.address = finaladdress;
MemoryBlock.size = size;
return MemoryBlock.address;
}
}
//If you arrive here, that means there is no empty memory block in the Allocated_MemoryBlocks array!
std::cout << "There is no empty memory block in the Allocated_MemoryBlocks array, so nullptr is returned!\n";
return nullptr;
}
TMemoryManager::TMemoryManager(size_t Main_MemoryBlockSize) {
if (SELF != nullptr) {
std::cout << "You shouldn't create a MemoryManager!";
return;
}
std::cout << "TMemoryManager is created!\n";
MainMemoryBlock.address = malloc(Main_MemoryBlockSize);
MainMemoryBlock.size = Main_MemoryBlockSize;
SELF = this;
std::cout << "Main Memory Block's start pointer: " << MainMemoryBlock.address << std::endl;
MemoryBlockInfo TuranAPI_MemoryPool;
TuranAPI_MemoryPool.address = MainMemoryBlock.address;
std::cout << "TuranAPI_MemoryPool.address: " << TuranAPI_MemoryPool.address << std::endl;
TuranAPI_MemoryPool.size = 1024 * 1024 * 10;
TAPIMemoryAllocator Create(TuranAPI_MemoryPool);
}
TMemoryManager* TMemoryManager::SELF = nullptr;
TMemoryManager First(1024 * 1024 * 1024 * 1);
Main.cpp
String FirstString("How are you?", TAPIMemoryAllocator::SELF);
std::cout << FirstString << std::endl; //If I delete the below, it prints "How are you?" as expected
String SecondString("I'm fine, thanks!", TAPIMemoryAllocator::SELF);
std::cout << SecondString << std::endl;
Solved: The problem was in Allocator. When allocator goes out of scope, it's Allocate_MemoryBlock function (it's a virtual function, not static) is deleted. I don't know why it doesn't occur when only one String is created (maybe a compiler optimization) but storing Allocator's itself (All of variables was static already) and assinging SELF as stored one's pointer solved the problem.

Issues with strings and char arrays in C++

I'm writing a registry generator as a part of a bigger program. I'm very new in C++, but good at other programming languages like PHP.
I'll start by providing the code of the problematic function:
void generacionAleatoria() {
string r_marca, r_nom, r_apellido;
char r_patente[6];
int num_rand;
registroAuto r_auto;
string nombres[8] = {
"Juan", "Pedro", "Roberto", "Miguel", "Guillermo", "Emilio", "Roque", "Gustavo"
} ;
string apellidos[8] = {
"Messi", "Maradona", "Gardel", "Heredia", "Pimpinela", "Nadal", "Mascherano", "Troilo"
};
string marcas[12] = {
"Volvo", "Renault", "Audi", "Ford", "Fiat", "Chevrolet", "Nissan", "Volkswagen", "Mercedes Benz", "Rolls Royce", "Delorean", "Aston Martin"
};
char letras_patentes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char numeros_patentes[] = "0123456789";
for (int i = 0; i < cantidad_autos; i++) {
r_marca = marcas[rand() % (sizeof(marcas)/sizeof(marcas[0]) - 1)];
r_nom = nombres[rand() % (sizeof(nombres)/sizeof(nombres[0]) - 1)];
r_apellido = apellidos[rand() % (sizeof(apellidos)/sizeof(apellidos[0]) - 1)];
for(int m = 0; m < 3; ++m) {
r_patente[m] = letras_patentes[rand() % (sizeof(letras_patentes) - 1)];
}
for(int n = 3; n < 6; n++) {
r_patente[n] = numeros_patentes[rand() % (sizeof(numeros_patentes) - 1)];
}
strcpy(r_auto.patente,r_patente);
strcpy(r_auto.marca,r_marca.c_str());
strcpy(r_auto.apellido,r_apellido.c_str());
strcpy(r_auto.nom,r_nom.c_str());
fwrite(&r_auto,sizeof(registroAuto),1,archivo);
if (ver_variables_testeo) {
//cout << (i+1) << ") " << r_auto.patente<<endl;
cout << (i+1) << ") " << r_auto.marca << " - " << r_auto.patente << " - " << r_auto.nom << " " << r_auto.apellido << endl; //Para testear
}
}
}
This creates 100 structs of the following type:
struct registroAuto {
char marca[15];
char patente[6];
char nom[25];
char apellido[25];
};
In case you're wondering, this is meant to be a registry of Uber drivers and their cars: brand, license plate, name and surname. Well, it's not really a registry, it's college homework.
The problem is that when I print out the contents of my new struct, the license plate and the name will be together, as in:
100) Fiat - KWQ293Maria - Maria Gardel
You can see by the position of the hyphens, that the license plate is now "KWQ293Maria", even though it is an array of 6 chars!
A reminder of the cout command:
cout << (i+1) << ") " << r_auto.marca << " - " << r_auto.patente << " - " << r_auto.nom << " " << r_auto.apellido << endl;
I did some tests, but I don't know what to do with the results.
1: commenting out the strcopy of the name fixes the issue
strcpy(r_auto.patente,r_patente);
strcpy(r_auto.marca,r_marca.c_str());
strcpy(r_auto.apellido,r_apellido.c_str());
//strcpy(r_auto.nom,r_nom.c_str());
As you can see, this is the last of the 4 statements in my original code, so I don't know why it would affect r_auto.patente.
Can you please help me? I'm guessing there's a key concept of char array handling that I missed out on in class :-(
When using character arrays as strings they need to be terminated by a null character '\0'. So when you construct your number-plate you need to make the array 7 characters long.
struct registroAuto {
char marca[15];
char patente[7]; // 6 for numbers, 1 for terminator '\0'
char nom[25];
char apellido[25];
};
Same with your working variable:
char r_patente[7];
And you need to manually add the null-terminator when you create the number:
for(int m = 0; m < 3; ++m) {
r_patente[m] = letras_patentes[rand() % (sizeof(letras_patentes) - 1)];
}
for(int n = 3; n < 6; n++) {
r_patente[n] = numeros_patentes[rand() % (sizeof(numeros_patentes) - 1)];
}
r_patente[6] = '\0'; // add the null terminator

Converting and Passing Arrays

I basically want to pass this array of data I'm reading to diffent functions and eventually plot it.
The array contains a 32 bit word of '1's and '0's, I then want to add these individual bits together to see where my data spikes. So in other words if I add "0100" to "0110" I get "0210" - which is probably easier done with separate bins and plotting.
At the moment I'm just getting garbage out.
void binary(int convert, int* dat) {
bitset<32> bits(convert);
//cout << bits.to_string() << endl;
char data[32];
for(unsigned i = 0; i < 32; ++i) {
data[i] = bits[i];
}
for(unsigned i = 32; i; --i) {
dat[i] = (int(data[i-1]))+dat[i];
}
}
void SerDi() {
int dat[32];
cout << " Reading data from memory..." << endl;
ValVector< uint32_t> data=hw.getNode("SerDi.RAM").readBlock(8);
hw.dispatch();
cout << data[0]<<endl;
cout << data[1]<<endl;
for (unsigned i = 2; i < 7; i++) {
binary(data[i], dat);
}
cout << dat[7] << endl;
graph(dat); //passes the array to a place where I can plot the graph
}
You have
int dat[32];
But in convert you have i = 32 and dat[i] That will access something outside of the array and bad things will happen.
Also that is not initialized. Add a memset/loop somewhere to make dat initially 0.

My buffer contains elements, but aren't being printed

Sorry scratch my last post, it's way to late =S
But basically I'm having problems sending out the buffer I created. Just need to know where I'm going wrong =( or if theres a better way.
------ Client Sending Username -------
int bufferSize = 32;
char messageBuffer[bufferSize];
char* message;
if (userName.size() > 8)
{
cout << "Invalid username : Greater than 8 characters" << endl;
}
else
{
switch(regType)
{
case(REGISTER):
{
cout << "Registered name: " << userName << endl;
messageBuffer[0] = 1;
messageBuffer[1] = 0;
for (int i = 2; i < (userName.size() + 2); i++)
{
messageBuffer[i] = userName[(i - 2)];
}
for (int i = 0; i < (userName.size() + 2); i++)
{
cout << messageBuffer[i];
}
cout << "<<<< messageBuffer " << endl;
message = &messageBuffer[0];
cout << messageBuffer << endl;
//message[(userName.size() + 2)] = '\0';
int messageLen = userName.size() + 2;
if (send(sock, messageBuffer, messageLen, 0) != messageLen)
DieWithError("send() send an invalid name");
}break;
case(UNREGISTER):
{
}break;
}
}
----------- Server (Receiver)------------
char msgRcvBuffer[RCVBUFSIZE];
int recvMsgSize;
if ((recvMsgSize = recv(clntSocket, msgRcvBuffer, RCVBUFSIZE, 0)) < 0)
DieWithError("recv() failed");
msgRcvBuffer[recvMsgSize] = '\0';
string msgType( msgRcvBuffer );
cout << "Message Type " << msgType << endl; <<<<<< Nothing appears when printed
void handleReg(string message, int socket, string ipAddr)
{
// Remove the Prefix
int startIndex = 2;
// Get the username from the message
string userName = message.substr(startIndex, message.size() - startIndex);
cout << "Username " << userName << endl;
For some reason my message string is just 1... =S What i'm trying to do is just get the message from what was sent from client. I'm just tryin to remove the '1' and '0' from the beginning of the buffer. 1 and 0 aren't characters.
Thanks so much for everyones help =)
The conversion from char* to string treats the string as null-terminated. This doesn’t seem to be the case here – in particular, your char array appears to contain 0 characters, so the string will be cut off at this position.
To circumvent this, you need to pass the valid range of characters to the string constructor, in place of only the start pointer:
msgType = string(msgRcvBuffer, msgRcvBuffer + recvMsgSize);
If msgRcvBuffer is of size RCVBUFSIZE then msgRcvBuffer[recvMsgSize] = '\0'; is going to be writing beyond the end of the buffer I think.
Use the std::string constructor that takes a buffer and buffer size parameter:
msgType = std::string(msgRcvBuffer, recvMsgSize);