Invalid read using valgrind - c

After a compilation with no warnings and errors for my file reorg.c, I am running the program using Valgrind and I am getting the following output. I am trying to understand why I am getting a segmentation fault but I can't really find something wrong with line 35:
Memcheck, a memory error detector
==29338== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==29338== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==29338== Command: ./reorg /opt/lsde/dataset-sf100/
==29338==
==29338== Invalid read of size 2
==29338== at 0x109398: main (reorg.c:35)
==29338== Address 0x12cb4008 is not stack'd, malloc'd or (recently) free'd
==29338==
==29338==
==29338== Process terminating with default action of signal 11 (SIGSEGV)
==29338== Access not within mapped region at address 0x12CB4008
==29338== at 0x109398: main (reorg.c:35)
==29338== If you believe this happened as a result of a stack
==29338== overflow in your program's main thread (unlikely but
==29338== possible), you can try to increase the size of the
==29338== main thread stack using the --main-stacksize= flag.
==29338== The main thread stack size used in this run was 8388608.
==29338==
==29338== HEAP SUMMARY:
==29338== in use at exit: 15,280 bytes in 10 blocks
==29338== total heap usage: 10 allocs, 0 frees, 15,280 bytes allocated
==29338==
==29338== LEAK SUMMARY:
==29338== definitely lost: 5,120 bytes in 5 blocks
==29338== indirectly lost: 0 bytes in 0 blocks
==29338== possibly lost: 0 bytes in 0 blocks
==29338== still reachable: 10,160 bytes in 5 blocks
==29338== suppressed: 0 bytes in 0 blocks
==29338== Rerun with --leak-check=full to see details of leaked memory
==29338==
==29338== For lists of detected and suppressed errors, rerun with: -s
==29338== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
This is the code of the reorg.c program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "utils.h"
Person *person_map;
unsigned int *knows_map;
unsigned short *interest_map;
unsigned long person_num = 0;
int main(int argc, char *argv[]) {
unsigned long file_length;
interest_map = (unsigned short *) mmapr(makepath(argv[1], "interest", "bin"), &file_length);
printf("amin1");
knows_map = (unsigned int *) mmapr(makepath(argv[1], "knows", "bin"), &file_length);
printf("amin2");
person_map = (Person *) mmapr(makepath(argv[1], "person", "bin"), &file_length);
printf("amin3");
knows_map = (unsigned int *) mmapr(makepath(argv[1], "knows", "bin"), &file_length);
printf("knows");
person_map = (Person *) mmapr(makepath(argv[1], "person", "bin"), &file_length);
printf("person");
person_num = file_length/sizeof(person_map);
int counter=0;
FILE* fp_knows2 = fopen(makepath(argv[1], "knows2", "bin"), (char*) "w");
FILE* fp_person2 = fopen(makepath(argv[1], "person2", "bin"), (char*) "w");
long knows2_bytesize;
int *knows2_map;
int *person2_map;
long person2_bytesize;
for(long i=0; i<person_num; i++) {
for(long j = 0; j < person_map[i].knows_n; j++) {
int friend = knows_map[person_map[i].knows_first+j]; // person in my knows-list
if (person_map[friend].location == person_map[i].location) {
counter++;
fwrite(&friend, 1,sizeof(int), fp_knows2);
}
}
if(counter > 0){
fwrite(&person_map[i], 1, sizeof(int), fp_person2);
}
counter=0;
}
fclose(fp_knows2);
fclose(fp_person2);
person2_map = (int*) mmapr(makepath(argv[1], "person2","bin"), &person2_bytesize);
knows2_map = (int*) mmapr(makepath(argv[1], "knows2","bin"), &knows2_bytesize);
return 0;
}
And this is the code of the utils.h program that am including in:
#define REPORTING_N 1000000
#define LINEBUFLEN 1024
typedef unsigned long byteoffset;
typedef unsigned int entrycount;
typedef struct {
unsigned long person_id;
unsigned short birthday;
unsigned short location;
unsigned long knows_first;
unsigned short knows_n;
unsigned long interests_first;
unsigned short interest_n;
} Person;
void parse_csv(char* fname, void (*line_handler)(unsigned char nfields, char** fieldvals)) {
long nlines = 0;
FILE* stream = fopen(fname, "r");
if (stream == NULL) {
fprintf(stderr, "Can't read file at %s\n", fname);
exit(-1);
}
char line[LINEBUFLEN];
char* tokens[10];
unsigned int col, idx;
tokens[0] = line;
while (fgets(line, LINEBUFLEN, stream)) {
col = 0;
// parse the csv line into array of strings
for (idx=0; idx<LINEBUFLEN; idx++) {
if (line[idx] == '|' || line[idx] == '\n') {
line[idx] = '\0';
col++;
tokens[col] = &line[idx+1];
} // lookahead to find end of line
if (line[idx+1] == '\0') {
break;
}
}
(*line_handler)(col, tokens);
nlines++;
if (nlines % REPORTING_N == 0) {
printf("%s: read %lu lines\n", fname, nlines);
}
}
fclose(stream);
}
FILE* open_binout(char* filename) {
FILE* outfile;
outfile = fopen(filename, "wb");
if (outfile == NULL) {
fprintf(stderr, "Could not open %s for writing\n", filename);
exit(-1);
}
return outfile;
}
unsigned short birthday_to_short(char* date) {
unsigned short bdaysht;
char dmbuf[3];
dmbuf[2] = '\0';
dmbuf[0] = *(date + 5);
dmbuf[1] = *(date + 6);
bdaysht = atoi(dmbuf) * 100;
dmbuf[0] = *(date + 8);
dmbuf[1] = *(date + 9);
bdaysht += atoi(dmbuf);
return bdaysht;
}
void* mmapr(char* filename, byteoffset *filelen) {
int fd;
struct stat sbuf;
void *mapaddr;
if ((fd = open(filename, O_RDONLY)) == -1) {
fprintf(stderr, "failed to open %s\n", filename);
exit(1);
}
if (stat(filename, &sbuf) == -1) {
fprintf(stderr, "failed to stat %s\n", filename);
exit(1);
}
mapaddr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (mapaddr == MAP_FAILED) {
fprintf(stderr, "failed to mmap %s\n", filename);
exit(1);
}
*filelen = sbuf.st_size;
return mapaddr;
}
char* makepath(char* dir, const char* file, const char* ext) {
char* out = (char*) malloc(1024), *sep = (char*) "";
if (strlen(dir) && dir[strlen(dir)-1] != '/') sep = (char*) "/";
sprintf(out, "%s%s%s.%s", dir, sep, file, ext);
return out;
}

Related

simple c program output as expected, but valgrind memory lost

Context: Explanation of pointing to same address problem
compile:
gcc \
-Wpointer-arith -Wendif-labels \
-Wimplicit-fallthrough -Wmissing-format-attribute \
-Wcast-function-type -Wshadow=compatible-local \
-Wformat-security -Wno-format-truncation -Wno-stringop-truncation \
main.c &&./a.out dat/test.csv
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<errno.h>
enum {MAXB = 128, NELEM = 10};
typedef struct data_t{
char *name;
int id;
double balance;
}data_t;
int main(int argc,char **argv)
{
if(argc < 2){
printf("No file input.foramt a.out filename. %s\n",argv[1]);
exit(EXIT_FAILURE);
}
FILE *fp = fopen(argv[1],"r");
if(fp == NULL){
printf("file not found\n");
exit(EXIT_FAILURE);
}
char buffer[MAXB] = "";
const char *delim = ",\n";
int index = 0;
data_t **array = malloc(NELEM * sizeof *array);
if(!array){ /* validate EVERY allocation */
perror("malloc-array failed\n");
exit(EXIT_FAILURE);
}
/* protect the allocation bounds, you only have NELEM pointers */
while (index < NELEM && fgets(buffer,sizeof buffer,fp))
{
data_t customer;
size_t len = 0;
char *tmp; /* temporary pointer to use w/strtok() */
tmp = strtok(buffer,delim); /* token for name */
if(!tmp) /* validate token */
{
fputs("error: strtok() name.\n",stderr);
exit(EXIT_FAILURE);
}
len = strlen(tmp); /* get length */
customer.name = malloc(len+1); /* allocate for string */
if(!customer.name)
{
perror("malloc-customer.name\n");
exit(EXIT_FAILURE);
}
memcpy(customer.name,tmp, len +1); /* copy tmp to .name */
if(!(tmp = strtok(NULL,delim))) /* token & validations */
{
fprintf(stderr,"error: strtok() -id.\n");
exit(EXIT_FAILURE);
}
/* MINIMAL conversion validation with sscanf() */
if(sscanf(tmp,"%d",&customer.id) != 1){
fprintf(stderr,"error: invalid integer value for line[%d]\n",index+1);
continue;
/* exit(EXIT_FAILURE); */
}
if(!(tmp = strtok(NULL,delim))){
fprintf(stderr,"\nerror: strtok()-balance shuld have more line\n");
continue;
// exit(EXIT_FAILURE);
}
if(sscanf(tmp,"%lf", &customer.balance) != 1){
fprintf(stderr,"error: invalid float value for line[%d]\n", index+1);
/* exit(EXIT_FAILURE); */
continue;
}
array[index] = malloc(sizeof *array[index]); /* allocate struct */
if(!array[index]){ /* validate!! */
perror("malloc-array[index]");
exit(EXIT_FAILURE);
}
*array[index] = customer; /* assign struct */
index += 1;
}
fclose(fp);
int i;
printf("\nname\tid\t balance\n\n");
for(i = 0; i< index; i++)
printf("%s \t%d\t%f\n",array[i]->name,array[i]->id,array[i]->balance);
for(i = 0; i< index; i++)
{
if(array[i]->name)
free(array[i]->name); /* free allocated block for name */
if(array[i])
free(array[i]); /* free allocated array index */
}
free(array);
exit(EXIT_FAILURE);
}
cat dat/test.csv returns
name,id,balance
test1,11,1.2334
test2,12,1.133
test3,13,1.3334
test4,14,1.4334
command valgrind -s --leak-check=full ./a.out dat/test.csv output:
==494197== Memcheck, a memory error detector
==494197== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==494197== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==494197== Command: ./a.out dat/test.csv
==494197==
error: invalid integer value for line[1]
name id balance
test1 11 1.233400
test2 12 1.133000
test3 13 1.333400
test4 14 1.433400
==494197== Memcheck, a memory error detector
==494197== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==494197== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==494197== Command: ./a.out dat/test.csv
==494197==
error: invalid integer value for line[1]
name id balance
test1 11 1.233400
test2 12 1.133000
test3 13 1.333400
test4 14 1.433400
==494197==
==494197== HEAP SUMMARY:
==494197== in use at exit: 5 bytes in 1 blocks
==494197== total heap usage: 13 allocs, 12 frees, 5,797 bytes allocated
==494197==
==494197== 5 bytes in 1 blocks are definitely lost in loss record 1 of 1
==494197== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==494197== by 0x10951D: main (in /home/jian/helloc/a.out)
==494197==
==494197== LEAK SUMMARY:
==494197== definitely lost: 5 bytes in 1 blocks
==494197== indirectly lost: 0 bytes in 0 blocks
==494197== possibly lost: 0 bytes in 0 blocks
==494197== still reachable: 0 bytes in 0 blocks
==494197== suppressed: 0 bytes in 0 blocks
==494197==
==494197== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

POSIX semaphores - cannot determine what causes segmentation fault

I'm working on the sleeping barber problem (using FIFO queue and shared memory), and I have a problem. I try to run this program just to see what is shown, however, I get segmentation fault every time. In the code I check, if semaphores are created successfully, if shared memory is created properly, but the program crashing must come from different place, which I cannot seem to find. I also tried to use Valgrind but what I got is:
Valgrind for the newer version of code:
However, when I try to run the code I still get segfault
==187== Memcheck, a memory error detector
==187== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==187== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==187== Command: ./barber
==187==
Error while executing program, invalid amount of arguments==187==
==187== HEAP SUMMARY:
==187== in use at exit: 0 bytes in 0 blocks
==187== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==187==
==187== All heap blocks were freed -- no leaks are possible
==187==
==187== For lists of detected and suppressed errors, rerun with: -s
==187== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==124== Memcheck, a memory error detector
==124== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==124== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==124== Command: ./barber
==124==
==124== Invalid read of size 1
==124== at 0x1094DD: main (in /mnt/c/users/Czaro/ubuntu/lab6posix/barber)
==124== Address 0x2 is not stack'd, malloc'd or (recently) free'd
==124==
==124==
==124== Process terminating with default action of signal 11 (SIGSEGV)
==124== Access not within mapped region at address 0x2
==124== at 0x1094DD: main (in /mnt/c/users/Czaro/ubuntu/lab6posix/barber)
==124== If you believe this happened as a result of a stack
==124== overflow in your program's main thread (unlikely but
==124== possible), you can try to increase the size of the
==124== main thread stack using the --main-stacksize= flag.
==124== The main thread stack size used in this run was 8388608.
glut==124==
==124== HEAP SUMMARY:
==124== in use at exit: 125 bytes in 4 blocks
==124== total heap usage: 5 allocs, 1 frees, 1,149 bytes allocated
==124==
==124== LEAK SUMMARY:
==124== definitely lost: 0 bytes in 0 blocks
==124== indirectly lost: 0 bytes in 0 blocks
==124== possibly lost: 0 bytes in 0 blocks
==124== still reachable: 125 bytes in 4 blocks
==124== suppressed: 0 bytes in 0 blocks
==124== Reachable blocks (those to which a pointer was found) are not shown.
==124== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==124==
==124== For lists of detected and suppressed errors, rerun with: -s
==124== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault
I'd greatly appreciate every kind of help!
The code for barber (newer version, with changes given by #AndrewHenle looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "functions.h"
#include <sys/mman.h>
#include <unistd.h>
int main(char* argv[], int argc)
{
if(argc < 2){
printf("Error while executing program, invalid amount of arguments");
return 0;
}
sem_t *barber;
sem_t *queue;
int seats;
int sharedmem, waitRoomSize;
struct Queue waitroom;
barber = sem_open("Barber", O_CREAT | O_RDWR, 0666, 1);
if((barber == SEM_FAILED)){
printf("Error while creating semaphore for barber");
exit(1);
}
queue = sem_open("Queue", O_CREAT | O_RDWR, 0666, 1);
if((queue == SEM_FAILED)) {
printf("Error while creating semaphore for queue");
exit(1);
}
seats = atoi(argv[1]);
void *space;
queueinit(waitroom, seats);
sharedmem = shm_open("Queue", O_RDWR, 0666);
if(sharedmem==-1){
printf("Error while getting shared memory");
exit(1);
}
waitRoomSize = ftruncate(sharedmem, sizeof(waitroom));
if((waitRoomSize ==-1)){
printf("Error while getting size");
exit(1);
}
space = mmap(NULL, sizeof(struct Queue), PROT_READ | PROT_WRITE, MAP_SHARED, sharedmem, 0);
if((space == MAP_FAILED)){
printf("B³¹d podczas mapowania pamiêci");
exit(1);
}
while(1)
{
printf("Starting work...");
sem_wait(queue);
sem_post(queue);
if(isEmpty(waitroom)) {
sem_wait(barber);
printf("Falling asleep \n");
sem_post(barber);
printf("Waking up \n");
}
sem_wait(queue);
sem_post(queue);
int id = get(waitroom);
printf("Customer: %d, please sit on the chair", id);
printf("I start making haircut: %d", id);
sleep(2);
printf("Finished making haircut for: %d", id);
}
sem_close(barber);
sem_unlink("Barber");
sem_close(queue);
sem_unlink("Queue");
}
Previous version:
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "functions.h"
#include <sys/mman.h>
#include <unistd.h>
int main(char* argv, int argc)
{
sem_t *barber;
sem_t *queue;
int seats;
int sharedmem, waitRoomSize;
struct Queue waitroom;
barber = sem_open("Barber", O_CREAT | O_RDWR, 0666, 1);
if((barber == SEM_FAILED)){
printf("Error while creating semaphore for barber");
exit(1);
}
queue = sem_open("Queue", O_CREAT | O_RDWR, 0666, 1);
if((queue == SEM_FAILED)) {
printf("Error while creating semaphore for queue");
exit(1);
}
seats = atoi(argv[1]);
void *space;
queueinit(waitroom, seats);
if((sharedmem = shm_open("Queue", O_RDWR, 0666)==-1)){
printf("Error while getting shared memory");
exit(1);
}
if((waitRoomSize = ftruncate(sharedmem, sizeof(waitroom))==-1)){
printf("Error while getting size");
exit(1);
}
if((space = mmap(NULL, sizeof(struct Queue), PROT_READ | PROT_WRITE, MAP_SHARED, sharedmem, 0) == MAP_FAILED)){
printf("Błąd podczas mapowania pamięci");
exit(1);
}
while(1)
{
printf("Starting work...");
sem_wait(queue);
sem_post(queue);
if(isEmpty(waitroom)) {
sem_wait(barber);
printf("Falling asleep \n");
sem_post(barber);
printf("Waking up \n");
}
sem_wait(queue);
sem_post(queue);
int id = get(waitroom);
printf("Customer: %d, please sit on the chair", id);
printf("I start making haircut: %d", id);
sleep(2);
printf("Finished making haircut for: %d", id);
}
sem_close(barber);
sem_unlink("Barber");
sem_close(queue);
sem_unlink("Queue");
}
The FIFO queue is implemented like this:
#ifndef FUNCTIONS_H_INCLUDED
#define FUNCTIONS_H_INCLUDED
#include <stdlib.h>
#include <unistd.h>
struct Queue{
int* elems;
int qsize;
int head;
int tail;
int capacity;
};
void queueinit(struct Queue q, int capacity)
{
q.elems = malloc(capacity*(sizeof(int)));
q.capacity = capacity;
q.head = 0;
q.qsize = 0;
q.tail = capacity-1;
}
int isFull(struct Queue q)
{
return (q.qsize == q.capacity);
}
int isEmpty(struct Queue q)
{
return (q.qsize == 0);
}
void push(struct Queue q, int val)
{
if(isFull(q)){
printf("Kolejka pe³na");
return;
}
q.tail = (q.tail +1) % q.capacity;
q.elems[q.tail] = val;
q.qsize = q.qsize +1;
printf("%d zajmuje miejsce w kolejce", val);
}
int get(struct Queue q)
{
if(isEmpty(q))
{
printf("kolejka pusta");
return -1;
}
int val = q.elems[q.head];
q.head = (q.head +1) % q.capacity;
q.qsize = q.qsize -1;
printf("%d opuszcza zak³ad", val);
}
#endif // FUNCTIONS_H_INCLUDED

(C) popen(79172,0x114108dc0) malloc: *** error for object 0x7ffee0155ad0: pointer being freed was not allocated and another error

What do these errors mean and how can I fix them?
popen(79172,0x114108dc0) malloc: *** error for object 0x7ffee0155ad0: pointer being freed was not allocated
popen(79172,0x114108dc0) malloc: *** set a breakpoint in malloc_error_break to debug
Here is the full code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
while (1){
char* command; //= (char *) malloc(128*sizeof(char));
char* output = (char *) malloc(4096);
printf(">> ");
scanf("%[^\n]", command);
FILE* cmd = popen(command, "r");
fread(output, sizeof(output), sizeof(cmd), cmd);
fputs(output, stdout);
if (strlen(output) != 0){
printf("\n%s\n", output);
free(output);
free(command);
pclose(cmd);
}
}
}
and here is the I/O:
>> ls
Applications
Creative Cloud Files
Desktop
Documents
Downloads
Dr
Applications
Creative Cloud Files
Desktop
Documents
Downloads
Dr
popen(79172,0x114108dc0) malloc: *** error for object 0x7ffee0155ad0: pointer being freed was not allocated
popen(79172,0x114108dc0) malloc: *** set a breakpoint in malloc_error_break to debug
Abort trap: 6
logout
Saving session...
...copying shared history...
...saving history...truncating history files...
popen(79172,0x114108dc0) malloc: *** error for object 0x7ffee0155ad0: pointer being freed was not allocated
popen(79172,0x114108dc0) malloc: *** set a breakpoint in malloc_error_break to debug
command is not initialized, the behavior is undefined when it is used by scanf and of course after in popen
I encourage you to compile with option for warning, for instance :
bruno#bruno-XPS-8300:/tmp$ gcc -Wall p.c
p.c: In function ‘main’:
p.c:10:9: warning: ‘command’ is used uninitialized in this function [-Wuninitialized]
scanf("%[^\n]", command);
^
bruno#bruno-XPS-8300:/tmp$
An other problem in that line is even command initialized to point to a block of memory you do not protect the length set by sscanf so it may write out of the buffer with an undefined hebavior
This line is also invalid
fread(output, sizeof(output), sizeof(cmd), cmd);
output is declared as a char* so sizeof return the size of a pointer whatever the size of the allocated block it may value, and independently of that this is not compatible with sizeof(cmd) whose must be a number of element.
After when you read the result with fread(output, sizeof(output), sizeof(cmd), cmd); you cannot be sure you read all , you need to use a loop up to EOF
When you use a function like scanf, fread, popen etc I encourage you to check the return value to detect error
Here an example :
#include <stdio.h>
int main(int argc, char ** argv)
{
char cmd[256];
if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
puts("EOF");
return -1;
}
FILE * fp = popen(cmd, "r");
if (fp != 0) {
char s[64];
while (fgets(s, sizeof(s), fp) != NULL)
fputs(s, stdout);
pclose(fp);
}
return 0;
}
Compilation and executions:
bruno#bruno-XPS-8300:/tmp$ gcc -Wall c.c
bruno#bruno-XPS-8300:/tmp$ ./a.out
date
samedi 11 avril 2020, 22:59:23 (UTC+0200)
bruno#bruno-XPS-8300:/tmp$ ./a.out
date | wc
1 6 42
bruno#bruno-XPS-8300:/tmp$ ./a.out
cat c.c
#include <stdio.h>
int main(int argc, char ** argv)
{
char cmd[256];
if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
puts("EOF");
return -1;
}
FILE * fp = popen(cmd, "r");
if (fp != 0) {
char s[64];
while (fgets(s, sizeof(s), fp) != NULL)
fputs(s, stdout);
pclose(fp);
}
return 0;
}
bruno#bruno-XPS-8300:/tmp$
If you absolutely want to allocate in the heap you can do for instance :
#include <stdio.h>
#include <stdlib.h>
#define LENGTH 256
int main(int argc, char ** argv)
{
char * buffer = malloc(LENGTH);
if (buffer == NULL) {
puts("not enough memory");
return -1;
}
if (fgets(buffer, LENGTH, stdin) == NULL) {
puts("EOF");
free(buffer);
return -1;
}
FILE * fp = popen(buffer, "r");
if (fp != 0) {
while (fgets(buffer, LENGTH, fp) != NULL)
fputs(buffer, stdout);
pclose(fp);
}
free(buffer);
return 0;
}
Compilation and execution :
bruno#bruno-XPS-8300:/tmp$ gcc -Wall c.c
bruno#bruno-XPS-8300:/tmp$ ./a.out
date
samedi 11 avril 2020, 23:07:01 (UTC+0200)
Execution under valgrind
bruno#bruno-XPS-8300:/tmp$ valgrind ./a.out
==12851== Memcheck, a memory error detector
==12851== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==12851== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==12851== Command: ./a.out
==12851==
date
samedi 11 avril 2020, 23:07:14 (UTC+0200)
==12851==
==12851== HEAP SUMMARY:
==12851== in use at exit: 0 bytes in 0 blocks
==12851== total heap usage: 5 allocs, 5 frees, 6,656 bytes allocated
==12851==
==12851== All heap blocks were freed -- no leaks are possible
==12851==
==12851== For counts of detected and suppressed errors, rerun with: -v
==12851== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
bruno#bruno-XPS-8300:/tmp$

Memory leak in OpenSSL?

I use the following code to check a signature. For simplicity public key, message and signature are initialized in main.
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
int main(){
const unsigned char key[] = "-----BEGIN PUBLIC KEY-----\n"
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALulMfYFyX1kSm7oUxZyCWWhrBBcWrRA\n"
"V7LSz1PzMPxoxG3KS8H7PRKFkIk42yM8/vhobmcCmj7UM5572wWch50CAwEAAQ==\n"
"-----END PUBLIC KEY-----\n";
unsigned int key_len = sizeof(key);
const unsigned char data[] = {0x6d,0x65,0x73,0x65,0x0a};
unsigned int data_len = sizeof(data);
const unsigned char sig[] = {0xa9,0x29,0x81,0x07,0x8c,0xeb,0xf0,0x1b,0x2a,0x31,0xe5,0x60,0x94,0x8a,0x47,0x94,0x3a,0x8f,0x6b,
0x4e,0x85,0xb9,0xe7,0xe5,0x4a,0x6c,0x56,0x46,0xd1,0x80,0x15,0x57,0xce,0xcb,0x0a,0x3a,0x67,0x15,0xed,
0x68,0x03,0x58,0x99,0xa4,0x73,0x61,0xe3,0x30,0x85,0xff,0x89,0x7e,0x32,0xef,0x16,0xec,0x23,0x7f,0x14,
0xde,0xbf,0x53,0xe0,0x3a};
unsigned int sig_len = sizeof(sig);
EVP_PKEY* evp_pubkey = EVP_PKEY_new();
RSA* rsa_pubkey = NULL;
EVP_MD_CTX ctx;
BIO* bufio = BIO_new_mem_buf((void*)key, key_len);
if(bufio == NULL){
fprintf(stderr, "BIO not created.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 3;
}
evp_pubkey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL);
if(evp_pubkey == NULL){
fprintf(stderr, "evp_pubkey not created.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 4;
}
EVP_MD_CTX_init(&ctx);
if (!EVP_VerifyInit(&ctx, EVP_sha256())) {
fprintf(stderr, "EVP_SignInit: failed.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 5;
}
if (!EVP_VerifyUpdate(&ctx, data, data_len)) {
fprintf(stderr, "EVP_SignUpdate: failed.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 6;
}
if (!EVP_VerifyFinal(&ctx, sig, sig_len, evp_pubkey)) {
fprintf(stderr, "EVP_VerifyFinal: failed.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 7;
}
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 0;
}
After compiling with gcc evp.c -lssl -lcrypto and running valgrind --tool=memcheck --leak-check=full ./a.out on it I get some memory leaks:
==7492== 56 bytes in 1 blocks are definitely lost in loss record 9 of 12
==7492== at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7492== by 0x4E9AD77: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==7492== by 0x4F5D459: EVP_PKEY_new (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==7492== by 0x400C4C: main (in a.out)
==7492==
==7492== 120 bytes in 1 blocks are definitely lost in loss record 10 of 12
==7492== at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7492== by 0x4E9AD77: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==7492== by 0x4F55678: EVP_DigestInit_ex (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==7492== by 0x400D6A: main (in a.out)
Am I doing something wrong?
You are overwriting the pointer here
evp_pubkey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL);
so the reference to the pointer returned by EVP_PKEY_new() the first time is lost and you can't free it anymore.
What #iharob wrote in his answer and,
You used EVP_MD_CTX_init() but did not free the data it allocated using EVP_MD_CTX_cleanup(&ctx) or - depending on your openssl version - EVP_MD_CTX_free(&ctx).
And if you compile with debug info (using -g in gcc/clang) valgrind would show you the source code lines responsible for the leaks.
You could also use EVP_MD_CTX_create() instead of using a static EVP_MD_CTX.
Here is the updated code:
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
int main() {
const unsigned char key[] =
"-----BEGIN PUBLIC KEY-----\n"
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALulMfYFyX1kSm7oUxZyCWWhrBBcWrRA\n"
"V7LSz1PzMPxoxG3KS8H7PRKFkIk42yM8/vhobmcCmj7UM5572wWch50CAwEAAQ==\n"
"-----END PUBLIC KEY-----\n";
unsigned int key_len = sizeof(key) - 1;
const unsigned char data[] = { 0x6d,0x65,0x73,0x65,0x0a };
unsigned int data_len = sizeof(data);
const unsigned char sig[] = {
0xa9,0x29,0x81,0x07,0x8c,0xeb,0xf0,0x1b,0x2a,0x31,0xe5,0x60,
0x94,0x8a,0x47,0x94,0x3a,0x8f,0x6b,0x4e,0x85,0xb8,0xe7,0xe5,
0x4a,0x6c,0x56,0x46,0xd1,0x80,0x15,0x57,0xce,0xcb,0x0a,0x3a,
0x67,0x15,0xed,0x68,0x03,0x58,0x99,0xa4,0x73,0x61,0xe3,0x30,
0x85,0xff,0x89,0x7e,0x32,0xef,0x16,0xec,0x23,0x7f,0x14,0xde,
0xbf,0x53,0xe0,0x3a
};
unsigned int sig_len = sizeof(sig);
int ret = 0;
EVP_PKEY* evp_pubkey = NULL;
RSA* rsa_pubkey = NULL;
EVP_MD_CTX* evp_ctx;
BIO* bufio = NULL;
if (!(bufio = BIO_new_mem_buf((void*)key, key_len))) {
fprintf(stderr, "BIO not created.\n");
ret = 1;
goto out;
}
if (!(evp_pubkey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL))) {
fprintf(stderr, "evp_pubkey not created.\n");
ret = 2;
goto out;
}
if (!(evp_ctx = EVP_MD_CTX_create())) {
fprintf(stderr, "ctx not created.\n");
ret = 3;
goto out;
}
if (!EVP_VerifyInit(evp_ctx, EVP_sha256())) {
fprintf(stderr, "EVP_SignInit: failed.\n");
ret = 4;
goto out;
}
if (!EVP_VerifyUpdate(evp_ctx, data, data_len)) {
fprintf(stderr, "EVP_SignUpdate: failed.\n");
ret = 5;
goto out;
}
if (!EVP_VerifyFinal(evp_ctx, sig, sig_len, evp_pubkey)) {
fprintf(stderr, "EVP_VerifyFinal: failed.\n");
ret = 6;
goto out;
}
out:
if (evp_ctx)
EVP_MD_CTX_destroy(evp_ctx);
if (evp_pubkey)
EVP_PKEY_free(evp_pubkey);
if (bufio)
BIO_free(bufio);
return 0;
}

Why do I get a still reachable block after mallocing a char*?

I have the following code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
void print_usage()
{
printf("%s\n", "usage");
}
int file_exist (char *filename)
{
struct stat buffer;
return (stat (filename, &buffer) == 0);
}
int parse_parameters(int argc, char *argv[], char** in)
{
unsigned int i1 = 1; // 0 is the filename
for (; i1 < argc; ++i1)
{
if( 0 == strcmp("-h", argv[i1]) )
{
print_usage();
return 0;
}
else if( 0 == strcmp("-i", argv[i1]) )
{
*in = malloc( sizeof(char) * strlen(argv[++i1]) + 1 );
strcpy(*in, argv[i1]);
continue;
}
else
{
print_usage();
return 1;
}
}
return 0;
}
int main(int argc, char *argv[])
{
if( argc != 3 )
{
print_usage();
return 0;
}
char* in = NULL;
int parse = parse_parameters(argc, argv, &in);
if ( 0 != parse )
return parse;
printf("in: %s\n", in);
FILE* finput = NULL ;
if (file_exist(in))
finput = fopen(in, "r");
if (finput == NULL) {
perror("fopen");
exit(1);
}
free(in);
fclose(finput);
return 0;
}
After running it with valgrind with following parameters:
./main -i input
I get the following:
==30977== Memcheck, a memory error detector
==30977== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==30977== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==30977== Command: ./main -i input
==30977==
in: input
fopen: No such file or directory
==30977==
==30977== HEAP SUMMARY:
==30977== in use at exit: 6 bytes in 1 blocks
==30977== total heap usage: 2 allocs, 1 frees, 574 bytes allocated
==30977==
==30977== 6 bytes in 1 blocks are still reachable in loss record 1 of 1
==30977== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==30977== by 0x400946: parse_parameters (main.c:31)
==30977== by 0x4009E7: main (main.c:54)
==30977==
==30977== LEAK SUMMARY:
==30977== definitely lost: 0 bytes in 0 blocks
==30977== indirectly lost: 0 bytes in 0 blocks
==30977== possibly lost: 0 bytes in 0 blocks
==30977== still reachable: 6 bytes in 1 blocks
==30977== suppressed: 0 bytes in 0 blocks
==30977==
==30977== For counts of detected and suppressed errors, rerun with: -v
==30977== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Why is that ? If I try to pass in as char* then it won't get changed after the parse_parameters function.
Your program is exiting as a result of the exit (1) call, which occurs before you free (in). As a result you are seeing the valgrind message.

Resources