The company Tomasz worked for launched a new device line with more RAM than the previous generation. This was supposed to put an end to the sort of memory shortages common to embedded systems. However, it wasn't long before they began hearing from clients whose systems crashed whenever they attempted to upgrade the accompanying software package.
The initial reports were met with surprise and skepticism, but the investigation soon led Tomasz and his cohorts to—you guessed it—a reproducible out-of-memory error.
With RAM not an issue, they took a deeper look at the upgrade process itself. The 50MB upgrade package was supposed to be copied to a special directory so the OS could install it. In C++ on linux, this is a simple task. You could use splice()
on a newer linux kernel, or sendfile()
on an older one. You could also read and write one buffer at a time. Inefficient, but effective.
As you may already suspect, the developers who'd written the library for handling upgrades had taken a more brillant approach, shown below. readFile()
stores the entire file in a string in memory, and writeFile()
places it in the desired directory. With this implementation, any device could be brought to its knees with a large-enough upgrade file.
bool readFile(const char *filename, std::string &result)
{
result.clear();
FILE *fp=fopen(filename,"rb");
if (!fp)
{
return false;
}
const size_t buff_size = 4 * 1024;
char * buffer = (char *) malloc(buff_size);
if (!buffer)
{
fclose(fp);
return false;
}
size_t r = 0;
do
{
r=fread(buffer,1,buff_size,fp);
result.append(buffer,buffer+r);
}
while(r==buff_size);
free(buffer);
fclose(fp);
return true;
}
bool writeFile(const char *file, const std::string & data )
{
if(!file) return false;
FILE *fp=fopen(file,"wb");
if(!fp) return false;
fwrite( data.c_str(), sizeof( char ), data.size(), fp );
int r=ferror(fp);
fclose(fp);
return (r==0);
}