Segmentation fault after mapping a file to memory - c

I am trying to "mmap" a binary file in order to encrypt it using AES then write the encrypted data to another file(outFile) using the following code. I tried to modify the flags for both functions mmap() and open() but I always get segmentation fault when I run the executable.
int main (void)
{
FILE *outFile; //The output file (encrypted)
/* A 256 bit key */
unsigned char *key = (unsigned char *)"01234567890123456789012345678901";
/* A 128 bit IV */
unsigned char *iv = (unsigned char *)"01234567890123456";
int fd;
struct stat sb;
void * memblock;
fd = open("result0.jpg",O_RDONLY);
outFile=fopen("result0enc.jpg","wb");
fstat(fd, &sb);
printf("Size: %lu\n", sb.st_size);
unsigned char decryptedtext[sb.st_size];
int decryptedtext_len, ciphertext_len;
/* Initialise the library */
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
memblock = mmap(NULL, sb.st_size,PROT_READ, MAP_SHARED, fd, 0);
if (memblock == MAP_FAILED) {
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
ciphertext_len = encrypt((unsigned char *)memblock, sb.st_size,key,iv,ciphertext);
fwrite( ciphertext,1, sb.st_size,outFile);
if (munmap(memblock, sb.st_size) == -1) {
perror("Error un-mmapping the file");
/* Decide here whether to close(fd) and exit() or not. Depends... */
}
close(fd);
fclose(outFile);
EVP_cleanup();
ERR_free_strings();
return 0;
}

As yano mentioned in the comments, your error is here:
memcpy(outFile, ciphertext, sb.st_size);
You're trying to memcpy to a FILE * which is completely wrong. That doesn't do at all what you expect. You're overwriting the private internals of the FILE structure to which outFile points.
You should instead operate on a buffer and use fwrite to write to the file.
I suggest you get familiar with basic file I/O operations using f... functions before digging into mmap and encryption.

Related

Segmentation Fault (Core Dumped) using read() and write()

I've been out of programming in C for almost 2 years and have recently gotten an assignment in school on using write() and read().
Somewhere in the code I'm receiving the Segmentation Fault error, possibly on the filecopy function is where I'd put my money on. I was trying GDB but I haven't used that since that last time I programmed in C so I turn to here.
The code.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
void filecopy(int infd, int outfd);
int fd = -1;
char *prog = argv[0];
if(argc == 1)
filecopy(STDIN_FILENO, STDOUT_FILENO);
else
while(--argc > 0) {
if((fd = open(*++argv, O_RDONLY, "rb")) == -1) {
// we don't have fprintf... but we have sprintf =]
char tmp[30];
sprintf(tmp, "%s: can't open %s\0", prog, *argv);
write(STDOUT_FILENO, &tmp, sizeof(tmp));
exit(-1);
} else {
filecopy(fd, STDOUT_FILENO);
close(fd);
}
}
exit(0);
}
void filecopy(int infd, int outfd) {
// char *buf[1]; <-- causes unreadable characters outputted by write
char *buf;
while(read(infd, buf, 1) != -1)
write(outfd, buf, sizeof(buf));
}
The input/output
Thanks!
char *buf; is an uninitialized pointer, writing data through that pointer is
undefined behaviour.
char buf[1024];
ssize_t len;
while((len = read(infd, buf, sizeof buf)) != -1)
write(outfd, buf, len);
would be correct.
Note that char *buf[1]; is a array (of dimension 1) of pointers, that's
different to an array of chars. Using that you would need to do
read(infd, buf[0], somelength), but here again buf[0] would be an
uninitialized pointer and you would have the same problem. That's why declaring
an char array of say 1024 (you can choose another size) is the correct thing
to do.
Also in main use strlen(tmp) and not sizeof(tmp)
char tmp[30];
sprintf(tmp, "%s: can't open %s\0", prog, *argv);
write(STDOUT_FILENO, &tmp, strlen(tmp));
strlen returns you the length of the string which might be smaller than 29 and
if you use sizeof(tmp) you might be writing garbage past the end of the
string. Note also that 0 may be too small for the whole string, I'd use a
larger number or construct the string using snprintf:
snprintf(tmp, sizeof tmp, "%s: can't open %s\0", prog, *argv);
would be more safe.
Last thing:
while(--argc > 0)
if((fd = open(*++argv, O_RDONLY, "rb")) == -1) {
...
While this is correct, I feel that this code is awkward and hard to read. It
would be so much simpler to read if you did:
for(int i = 1; i < argc; ++i)
if((fd = open(argv[i], O_RDONLY, "rb")) == -1) {
...
I've never seen open being called with "rb" as the mode. My man page says:
man 2 open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
[...]
The mode argument specifies the file mode bits be applied when a new file is created. This argument must be supplied when
O_CREAT or O_TMPFILE is specified in flags; if neither O_CREAT nor O_TMPFILE is specified, then mode is ignored.
The effective mode is modified by the process's umask in the usual way: in the absence of a default ACL, the mode of the created file is
(mode & ~umask). Note that this mode applies only to future accesses of the newly created file; the open() call that creates a
read-only file may well return a read/write file descriptor.
The following symbolic constants are provided for mode:
S_IRWXU 00700 user (file owner) has read, write, and execute permission
S_IRUSR 00400 user has read permission
S_IWUSR 00200 user has write permission
S_IXUSR 00100 user has execute permission
[...]
As you are neither using O_CREAT nor O_TMPFILE, this parameter will be
ignore and you are passing a char* as a mode_t which is integer in nature.
Hence your call should be:
if((fd = open(argv[i], O_RDONLY, 0)) == -1) {
...
Two adjustments are needed for you filecopy function:
You need to allocate space for your buffer. Right now you are using an uninitialized pointer and passing it to read which is undefined behavior.
You need to save the return value of read and pass the value to write
The end result should look something like this.
void filecopy(int infd, int outfd) {
char buf[1024];
size_t bytes_read;
while((bytes_read = read(infd, buf, sizeof buf)) != -1)
write(outfd, buf, bytes_read);
}
Running this through a static analysis tool gives 2 warnings:
1) The uninitialized variable that #Pablo points to
2) a buffer overrun when you sprintf *argv into tmp as *argv can very large (as #Pablo also suggested in his comment re: snprintf)

How to Calculate MD5 of xls file in C language

I have made many researches about MD5 of an xls file but my effort seems be in vain
I tried to used lirary and recommendation in this link "https://stackoverflow.com/questions/27858288/calculate-md5-for-a-file-in-c-language"
but , still give wrong result ,
can you help me ??
Well I used to answer the link you gave but then the question was closed.
The idea is as follows. First read the file into a buffer. You can do this using following function:
unsigned char * readFile(const char *path)
{
FILE * pFile;
long lSize;
unsigned char * buffer;
size_t result;
pFile = fopen (path , "rb" );
if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file:
buffer = malloc (sizeof(char)*lSize);
if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
// copy the file into the buffer:
result = fread (buffer,1,lSize,pFile);
if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
// terminate
fclose (pFile);
return buffer;
}
Read the file
unsigned char * data = readFile("c:\\file.xls");
Then you must apply MD5 on this buffer of data. You can use code similar
to the one in that question (though I am not sure which library/implementation
of md5 author of that question used). e.g.,
char hash[64] = {0};
md5_byte_t digest[16] = {0};
md5_init(&state);
md5_append(&state, (const md5_byte_t *)data, filesize);
md5_finish(&state,digest);
int i=0;
for(i; i<16; i++)
{
snprintf(hash + i*2,sizeof(hash),"%02x",digest[i]);
}
Now hash should store the hash of the file, encoded in hexadecimal string. ps. Indeed that sample is incorrectly using strlen with binary file. That is why I suggested the readFile method above; that function also contains code to get file size - you can use that code to get file size and then pass the file size to md5_append method.
ps. also don't forget to free data when you are done with it.
MD5 of xls file is very same of MD5 of any other kind of file since it operates on bytes. See by example openssl implementation openssl/crypto/md5/md5.c and md5test.c ( code is in git://git.openssl.org/openssl.git ).
The problem is that your example uses strlen to determine the file size. But .xls format is binary, so strlen will not work properly.
Adapt the function to return the total data read from the file, and it should work.
Edit. Try something like this code:
void *addr;
struct stat s;
int ret, fd;
ret = stat(filename, &s);
if (ret) {
fprintf(stderr, "Error while stat()ing file: %m\n");
return -1;
}
fd = open(filename, O_RDONLY);;
if (fd < 0) {
fprintf(stderr, "Error while opening file: %m\n");
return -1;
}
addr = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) {
fprintf(stderr, "Error while mapping file: %m\n");
close(fd);
return -1;
}
md5_init(&state);
md5_append(&state,addr, s.st_size);
md5_finish(&state,digest);

How do store an encrypted file using C so that I can read the file and decrypt it?

I am trying to create a FUSE filesystem that encrypts files as they go in and then decrypts them as they come out. I started with a template that just called the appropriate system calls for most file operations then changed the read() and write() methods to call my encryption and decryption methods. When I write a file (using echo "readme" > /debug), I get the following output:
Encrypted Buffer: ??????}x?
Being written to: /debug
Locally Decrypted to: readme
The "locally decrypted" value is just to prove that the decryption method works appropriately. Next, I use cat /debug to try to read the file and it returns nonsense. Here are the debug statements I get:
Just read: ??????}
From: /debug
Decrypted to: ;?N?"Cs?
So you should see: ;?N?"Cs?
It looks to me like there is something going on when I either write my encrypted buffer to the file or when I try to read the encrypted buffer back out of the file. Could it have something to do with EOFs or the size parameter that each method takes in? Here are the read and write methods from my FUSE file:
static int pv_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
fd = open(dir_path(path), O_RDONLY);
if (fd == -1)
return -errno;
char *decrypt_buf = (char *) malloc(size);
char *raw_buf = (char *) malloc(size);
res = pread(fd, raw_buf, size, offset);
FILE *f = fopen("debug_read.txt", "w");
fprintf(f, "Just read: %s\nFrom: %s\n\n", raw_buf, path);
file_decrypt((byte *)raw_buf, size, (byte *)decrypt_buf);
fprintf(f, "Decrypted to: %s\n",decrypt_buf);
memcpy(buf, decrypt_buf, size);
fprintf(f, "So you should see: %s\n", buf);
fclose(f);
if (res == -1)
res = -errno;
close(fd);
return res;
}
static int pv_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
fd = open(dir_path(path), O_WRONLY);
if (fd == -1)
return -errno;
unsigned char *encrypt_buf = (unsigned char *) malloc(size);
unsigned char *d_buf = (unsigned char *) malloc(size);
file_encrypt((byte *)buf, size, (byte *)encrypt_buf);
file_decrypt((byte *) encrypt_buf, size, (byte *) d_buf);
FILE *f = fopen("debug_write.txt", "w");
fprintf(f, "Encrypted Buffer: %s\nBeing written to: %s\nLocally Decrypted to: %s\n", encrypt_buf, path,d_buf);
fclose(f);
res = pwrite(fd, encrypt_buf, size, offset);
if (res == -1)
res = -errno;
close(fd);
return res;
}
Thanks a lot!

Use of mmap causes invalid argument error

I am messing with mmap in c and I have encountered a very strange error. When I run the following block of code (which is sample code from this website)
/* The file descriptor. */
int fd;
/* Information about the file. */
struct stat s;
int status;
size_t size;
/* The file name to open. */
const char * file_name = "myfile.txt";
/* The memory-mapped thing itself. */
const void * mapped;
int i;
/* Open the file for reading. */
fd = open ("myfile.txt", O_RDONLY);
check (fd < 0, "open %s failed: %s", file_name, strerror (errno));
/* Get the size of the file. */
status = fstat (fd, & s);
check (status < 0, "stat %s failed: %s", file_name, strerror (errno));
size = s.st_size;
/* Memory-map the file. */
mapped = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0);
check (mapped == MAP_FAILED, "mmap %s failed: %s",
file_name, strerror (errno));
I am greeted with an Invalid Argument error.
My research has led me to conclude that this is an offset issue, but I am totally lost as to what I can do to fix it. Any advice would be greatly appreciated.
Thank you
I decided to try running this code on another machine and it worked fine, it appears to be a problem machine-side rather than a problem in the code. At least now I know the code isn't broken :)

Linux device driver, Is it possible to get the minor number using a file descriptor?

I am writing a device driver for Linux. It creates a device with 4 minor numbers. Whenever we attempt to write to the device at minor number 3, we are suppose to kill the device and currently it isn't suppose to do anything else except print it is writing to the booga device. Here is some of my current code and I can post more code if necessary:
Write method:
static ssize_t booga_write (struct file *filp, const char *buf, size_t count, loff_t *f_pose) {
printk("Attempting to write to booga device\n");
/* need to protect this with a semaphore if multiple processes
will invoke this driver to prevent a race condition */
if (down_interruptible (&booga_device_stats->sem))
return (-ERESTARTSYS);
booga_device_stats->num_bytes_written += count;
up(&booga_device_stats->sem);
return count; // pretend that count bytes were written
}
How it is tested:
static void run_write_test(char *device, int bufsize)
{
char *buf;
int src;
int out;
src = open(device, O_WRONLY);
if (src < 0) {
perror("Open for write failed:");
exit(1);
}
buf = (char *) malloc(sizeof(char)*(bufsize+1));
fprintf(stderr, "Attempting to write to booga device\n");
out = write(src, buf, bufsize);
fprintf(stderr, "Wrote %d bytes.\n", out);
free(buf);
close(src);
}
I am wondering if there is a way to get the minor number. I looked in linux/fs.h and saw that the file struct has a member called private_data but whenever I attempt to call this, it will crash my system as it is currently set to null.
Or should I not be trying to get the minor number from the struct file at all and should attempt to keep track of it when I first open the device?
You can get the minor number like so:
iminor(filp->f_path.dentry->d_inode)

Resources