retrieve information from a structure with ptrace - c

Here, I explain my problem, I am a beginner on the ptrace function and I would like to succeed in recovering the hard information of a structure.
For example with this command, I will have strace -e trace = fstat ls
a line: fstat (3, {st_mode = ..., st_size = ...}
and I would like to successfully retrieve the contents of the structure (st_mode) and (st_size).
I try this but to no avail:
int buffer(unsigned long long addr, pid_t child, size_t size, void *buffer)
{
size_t byte = 0;
size_t data;
unsigned long tmp;
while (byte < size) {
tmp = ptrace(PTRACE_PEEKDATA, child, addr + byte);
if ((size - byte) / sizeof(tmp))
data = sizeof(tmp);
else
data = size % sizeof(tmp);
memcpy((void *)(buffer + byte), &tmp, data);
byte += data;
}
}
and in params :
struct stat stat_i;
buffer(addr, pid, sizeof(stat_i), &stat_i);
printf("%lu", stat_i.st_size); -> fake value :/
Thank'ks !

From the man page,
PTRACE_PEEKTEXT, PTRACE_PEEKDATA
Read a word at the address addr in the tracee's memory,
returning the word as the result of the ptrace() call. Linux
does not have separate text and data address spaces, so these
two requests are currently equivalent. (data is ignored; but
see NOTES.)
Thus you must understand that tmp would hold the actually value that was read.
Your checks are wrong - you should set errno = 0 before the call and then check if it has changed. If it has - you've got an error. If it hasn't - you can be assured that tmp has the word from the remote process.
Try something like this:
int buffer(unsigned long long addr, pid_t child, size_t size, void *buffer)
{
size_t byte = 0;
size_t data;
unsigned long tmp;
// support for word aligned sizes only
if (size % sizeof(long) != 0)
return -1;
long * buffer_int = (long*) buffer;
while (byte < size) {
errno = 0;
tmp = ptrace(PTRACE_PEEKDATA, child, addr + byte);
if (errno)
return -1;
buffer_int[byte / sizeof(long)] = tmp;
byte += sizeof(long);
}
}

Related

zlib inflateReset causes memory leak (not)

I am currently working on the below requirement.
Here is the requirement: On the server side a large file is divided into 4000-byte blocks (frames). Each block is in turn compressed (using zlib) and sent to client process. For instance, if a file is 12000 bytes in size then it is divided into 3 blocks.
Above file will have 3 blocks => Block-0, Block-1, Block-2
On receipt, client decompresses each block (or frame) and writes to buffer allocated on the heap.When all the blocks corresponding to the entire file is received by the client, then the uncompressed version of the resultant file is written to the disk.
I have written a routine inflateData that does the following based on the block # received:
When the first block is received,
- inflateInit
- inflate
- inflateReset
When the intermediate blocks are received,
- inflate
- inflateReset
When the last block is received,
- inflate
- inflateEnd
With the above routine, Decompression of blocks happens as expected. But the issue that I face is it consumes lots of memory and at some point entire system slows down. When checked with valgrind, memory leak is reported with inflateInit2_. This causes the system resources to be exhausted.
==30359== 57,312 bytes in 6 blocks are possibly lost in loss record 64 of 67
==30359== at 0x4A069EE: malloc (vg_replace_malloc.c:270)
==30359== by 0x3E57808F1E: inflateInit2_ (in /lib64/libz.so.1.2.3)
==30359== by 0x40C220: inflateData (productMaker.c:1668)
Below is the routine inflateData.
int inflateData(
char* const inBuf,
unsigned long inLen,
unsigned int isFirstBlk,
unsigned int isLastBlk,
const char* outBuf,
unsigned long* outLen)
{
int have;
int readsz;
int bsize;
static z_stream zstrm;
int zerr;
int flush;
char out[CHUNK_SZ];
char in[CHUNK_SZ];
int ret,nwrite,idx = -1;
int savedByteCntr=0;
unsigned char *dstBuf;
int firstCall = 1;
int totalBytesIn=0;
int inflatedBytes=0;
int decompByteCounter = 0;
int num=0;
ret = Z_OK;
readsz = 0;
bsize = CHUNK_SZ;
dstBuf = (unsigned char *) outBuf;
if(isFirstBlk){
memset(&zstrm, '\0', sizeof(z_stream));
zstrm.zalloc = Z_NULL;
zstrm.zfree = Z_NULL;
zstrm.opaque = Z_NULL;
if ((zerr = inflateInit(&zstrm)) != Z_OK) {
uerror("ERROR %d inflateInit (%s)",
zerr, decode_zlib_err(zerr));
return -1;
}
}
while(totalBytesIn < inLen ) {
int compChunkSize = ((inLen - totalBytesIn) > 5120) ? 5120 :
(inLen - totalBytesIn);
memcpy(in, inBuf + totalBytesIn, compChunkSize);
zstrm.avail_in = inLen - totalBytesIn;
zstrm.next_in = in ;
zstrm.avail_out = CHUNK_SZ;
zstrm.next_out = out;
inflatedBytes = 0;
while(ret != Z_STREAM_END) {
ret = inflate(&zstrm, Z_NO_FLUSH);
if(ret < 0) {
uerror(" Error %d inflate (%s)", ret, decode_zlib_err(ret));
(void)inflateEnd(&zstrm);
return ret;
}
inflatedBytes = CHUNK_SZ - zstrm.avail_out;
if(inflatedBytes == 0) {
unotice("\n Unable to decompress data - truncated");
break;
}
totalBytesIn += zstrm.total_in;
decompByteCounter += inflatedBytes;
memcpy(dstBuf + savedByteCntr, out, inflatedBytes);
savedByteCntr = decompByteCounter;
}
// Reset inflater for additional input
ret = inflateReset(&zstrm);
if(ret == Z_STREAM_ERROR){
uerror(" Error %d inflateReset (%s)", ret, decode_zlib_err(ret));
(void)inflateEnd(&zstrm);
return ret;
}
}
if(isLastBlk){
ret = inflateEnd(&zstrm);
if(ret < 0) {
uerror("Fail inflateEnd %d [%s] ", ret, decode_zlib_err(ret));
return (ret);
}
}
*outLen = decompByteCounter;
return 0;
}
Thanks in advance for the support.
Thanks,
Sathya.
You are making an error in your use of your inflateData() routine.
First off, using a static variable in this way is a horrible idea. If you call your inflateData() twice with isFirstBlk true without an intermediate call with isLastBlk true, then you will wipe out the reference to the first set of allocations, resulting in a memory leak.
To avoid this sort of error, you should keep track of whether zstrm is initialized or not, and reject any attempt to initialize an already initialized stream. Better still would be to not even have an isFirstBlk, and simply initialize zstrm on the first call and on any call that immediately follows a call with isLastBlk true.
So you are either doing the above, calling twice with isFirstBlk true, or failing to call with isLastBlk true.

C - Memory access with mmap

I have a hex file of 327680 characters which I'm writing to physical address 0x30000000 - 0x3004FFFF on the memory on my ARM linux system.
While reading back from the memory I'm getting a segfault after reading 64170 characters from the start address, ie at 0x3000FAAA.
If I change my starting address to 0x3000FA64, then also I get a segfault after 64170 characters.
How do I ensure data is accessed correctly if Data > 4kB (page size) ?
I'm unable to understand the exact problem, so I'm adding the snippet of my code below:
#define MAX_RANGE 327679
int fd;
FILE* fd_table=NULL;
unsigned long int count = 0 ;
void * mem;
void * aligned_vaddr;
unsigned long aligned_paddr;
uint32_t aligned_size;
unsigned long int addr_phys;
uint8_t *addr;
int g_size = 1;
unsigned long int g_paddr = 0x30000000; //Starting physical address
while((count<MAX_RANGE)){
g_paddr = addr_phys;
g_paddr &= ~(g_size - 1);
aligned_paddr = g_paddr & ~(4096 - 1);
aligned_size = g_paddr - aligned_paddr + (g_count * g_size);
aligned_size = (aligned_size + 4096 - 1) & ~(4096 - 1);
/* Align address to access size */
aligned_vaddr = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, aligned_paddr);
if (aligned_vaddr == NULL) {
printf("Error mapping address\n");
close(fd);
return 1;
}
mem = (void *)((uint32_t)aligned_vaddr + (g_paddr - aligned_paddr));
addr = mem;
fprintf(fd_table, "%02X\n",addr[0]);
addr_phys +=1; //Increment byte address
count++;
}
Note:
1. There is no error in the write process, I have verified by viewing the segfault address with memtool.
2. The address 0x30000000 onwards is not used by the system (I have ensured that in the u-boot).

difference between reading proc files via shell (cat) or via program (fread)

I have a kernel module creating an entry in proc-fs and a userspace-program that reads the file.
My proc read-function looks like:
typedef struct {
int integer;
unsigned long ulong;
char string[100];
float floatt;
bool booll;
u16 crc;
} struktur;
static int round = 0, len = 0, temp = 0, err;
struktur *pde_data_p;
int proc_read(struct file *filp, char *buf, size_t count, loff_t *offp) {
int i;
unsigned char *crcbuf;
struktur struct_buf;
pde_data_p = PDE_DATA(file_inode(filp));
crcbuf = (unsigned char*)pde_data_p;
memcpy(&struct_buf, pde_data_p, sizeof(struktur));
if (pde_data_p == NULL) {
printk(KERN_ERR "pde->data == NULL\n");
round = 0;
return 0;
}
if (round == 0)
temp = sizeof(struktur);
if (count > temp)
count = temp;
struct_buf.crc = crc16(struct_buf.crc, crcbuf, sizeof(struktur)-sizeof(unsigned short));
err = copy_to_user(buf, pde_data_p, count);
//if (err == 0) { // copy_to_user finished
round = 0;
temp = 0; // taking this line out makes it work in the prog but not with my cat
return temp;
//} else { // copy_to_user failed -> return number of bytes that have not been copied
//temp = err;
//round++;
//return temp;
//}
}
My program code is:
typedef struct {
int integer;
unsigned long ulong;
char string[100];
float floatt;
bool booll;
unsigned short crc;
} struktur;
int main(void) {
int i;
struktur str_inp;
unsigned short crc = 0;
unsigned char *str_p = (unsigned char*)&str_inp;
FILE *fp = fopen("/proc/sen/entry", "r");
fread(&str_inp, 1, sizeof(struktur), fp);
fclose(fp);
}
As you can see my proc read-function returns the number of bytes read, not zero.
This way my program works fine but when I try to read via cat (cat /proc/sen/entry) it never finishes because it never returns 0.
When I change my code and return 0 after copy_to_user has finished, reading via cat works fine but my program seems reads random memory. When I return half of the number of copied bytes, just half of the data read by the user-space program is correct.
What has to be returned by your proc_read function depends on the position which is handed over in *offp. If it is only required to work for your given userspace program code and for cat (not for partial reads of struktur) it suffices to
if (*offp) return 0; // no more data to return
else *offp = sizeof (struktur); // file position after data returned
- this statement goes before the copy_to_user().

Issues with C in realloc() or free() or double free or corruption

I am trying to capture audio packets of variable size, strip the RTP header, then concatenate the audio data to 20 bytes each. My goal is to create something like a queue and just use pointer arithmetic to chop up data before I copy 20 bytes to a buffer. The issue occurs when I get a large amount of audio bytes coming into the queue(probably greater than 20). Here is the while loop that captures, copies to queue, and chops up the data:
run_flag = TRUE;
unsigned char *qs_ptr = NULL; //the very start of the queue
unsigned char *qcur_ptr = NULL; //the start of current audio packet
unsigned char *qsa_ptr = NULL; //the start of new incoming audio data
unsigned char *tmp_ptr = NULL; //points to the start of next audio packet to send
unsigned char audio_buf[20];
unsigned char buf[MAX_PACKET_LEN];
unsigned char *pkt_no_hdr = NULL;
int num_audio_bytes;
int tot_bytes;
int num_in_q;
/* listen for voip packets */
/* collection */
/* keeps track of audio bytes, send data when = 20 */
pf=fopen("rtp.dat","w");
while (run_flag==TRUE) {
if ((num_bytes = read(fd, buf, MAX_PACKET_LEN)) < 0) {
perror("recv");
close(sd);
exit(1);
}
pkt_no_hdr = (unsigned char *)calloc(num_bytes-12, sizeof(unsigned char));
/* remove 12 rtp header bytes */
num_audio_bytes = rem_rtp_hdr(pkt_no_hdr, &buf, num_bytes);
print_bytes(pkt_no_hdr, num_bytes-12);
printf("num audio bytes: %d\n", num_bytes-12);
tot_bytes+=num_audio_bytes;
num_in_q+=num_audio_bytes;
printf("num_in_q: %d\n", num_in_q);
cpy_to_q(&qs_ptr, &qcur_ptr, &qsa_ptr, pkt_no_hdr, num_audio_bytes, tot_bytes);
free(pkt_no_hdr);
if(num_in_q >= 20) {
tmp_ptr = qcur_ptr + 20;
memcpy(audio_buf, qcur_ptr, 20);
qcur_ptr = tmp_ptr;
print_bytes(audio_buf, 20);
// add header
// send mcast packet
num_in_q -= 20;
}
}
Here's the cpy_to_q function:
void cpy_to_q(unsigned char **qs_ptr, unsigned char **qcur_ptr, unsigned char **qsa_ptr, unsigned char *data, int num_bytes, int tot_bytes) {
if(*qs_ptr == NULL) {
*qs_ptr = (unsigned char *)malloc(num_bytes*sizeof(unsigned char) + 1);
*qcur_ptr = *qs_ptr;
*qsa_ptr = *qs_ptr;
memcpy(*qs_ptr, data, num_bytes);
} else {
*qs_ptr = (unsigned char *)realloc(*qs_ptr, tot_bytes*sizeof(unsigned char) + 1);
printf("size of q: %d\n", tot_bytes);
*qsa_ptr += num_bytes;
memcpy(*qsa_ptr, data, num_bytes);
}
}
I keep getting errors related to realloc() or free() which must happen in the cpy_to_q function:
\*** glibc detected \*** ./voipBridge: free(): invalid next size (fast): 0x000000000213b5b0 \***
Here is what valgrind says when the issue occurs:
Thread 1: status = VgTs_Runnable
==3799== at 0x4C2B4F0: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3799== by 0x401211: cpy_to_q (handle_q.c:21)
==3799== by 0x40177A: rcv_enter code herertp (net_interface.c:142)
==3799== by 0x401D89: main (voip_bridge.c:48)
Here is what the log says before the issue:
num audio bytes: 6 n
num_in_q: 14
REALLOC
size of q: 94
bytes: 0bd2d4b5da0f
num audio bytes: 6
num_in_q: 20
REALLOC
size of q: 100
bytes: b15c0f0b86f3b15a0f0bd2d4b5da0f0000000000
bytes: 08cb24ad9a0f
num audio bytes: 6
num_in_q: 6
REALLOC
size of q: 106
bytes: 22c6a0d000e3980ba0f27ccca4336ef243e3168e57150fd6e388b8c7bf
num audio bytes: 29
num_in_q: 35
REALLOC
size of q: 135
*** glibc detected *** ./voipBridge: double free or corruption (out): 0x00000000023432f0 ***
Also, I realize the queue will just keep getting larger and larger. Is there a way to free up memory without freeing the entire block?
Thanks.
This is a significant problem:
void cpy_to_q(unsigned char **qs_ptr, unsigned char **qcur_ptr, unsigned char **qsa_ptr,
unsigned char *data, int num_bytes, int tot_bytes) {
if(*qs_ptr == NULL) {
*qs_ptr = (unsigned char *)malloc(num_bytes*sizeof(unsigned char) + 1);
*qcur_ptr = *qs_ptr;
*qsa_ptr = *qs_ptr;
memcpy(*qs_ptr, data, num_bytes);
} else {
// HERE YOU REALLOC THE BASE PTR, BUT DON"T REPOS THE CUR PTR
*qs_ptr = (unsigned char *)realloc(*qs_ptr, tot_bytes*sizeof(unsigned char) + 1);
printf("size of q: %d\n", tot_bytes);
*qsa_ptr += num_bytes;
memcpy(*qsa_ptr, data, num_bytes);
}
}
When the allocation finally becomes significant enough to warrant a new full page alloc, realloc() will work, but now you have a current-pointer still pointing to an old queue that doesn't even exist anymore.
To fix this, keep the delta from the current queue in a tmp size var, then base the new cur_ptr off the new queue-base after the realloc. The same housekeeping logic is needed for the sa ptr, btw.
So something like this, and note this assumes your list always grows and has an entirely separate reset() or shrink() mechanism.
void cpy_to_q
(
unsigned char **qs_ptr,
unsigned char **qcur_ptr,
unsigned char **qsa_ptr,
unsigned char *data,
int num_bytes,
int tot_bytes
)
{
if(*qs_ptr == NULL)
{
*qs_ptr = malloc(num_bytes*sizeof(unsigned char) + 1);
*qcur_ptr = *qs_ptr;
*qsa_ptr = *qs_ptr;
memcpy(*qs_ptr, data, num_bytes);
}
else
{
size_t cur_diff = *qcur_ptr - *qs_ptr;
size_t sa_diff = *qsa_ptr - *qs_ptr;
// now realloc (note: you really should error check this)
*qs_ptr = realloc(*qs_ptr, tot_bytes*sizeof(unsigned char) + 1);
printf("size of q: %d\n", tot_bytes);
// now reposition your old pointers.
*qcur_ptr = *qs_ptr + cur_diff;
*qsa_ptr = *qs_ptr + sa_diff;
// and finally continue as before
*qsa_ptr += num_bytes;
memcpy(*qsa_ptr, data, num_bytes);
}
}

Memory comparison causes system halt

I am working on a kernel module and I need to compare two buffers to find out if they are equivalent. I am using the memcmp function defined in the Linux kernel to do so. My first buffer is like this:
cache_buffer = (unsigned char *)vmalloc(4097);
cache_buffer[4096] = '/0';
The second buffer is from a page using the page_address() function.
page = bio_page(bio);
kmap(page);
write_buffer = (char *)page_address(page);
kunmap(page);
I have printed the contents of both buffers before hand and not only to they print correctly, but they also have the same content. So next, I do this:
result = memcmp(write_buffer, cache_buffer, 2048); // only comparing up to 2048 positions
This causes the kernel to freeze up and I cannot figure out why. I checked the implementation of memcmp and saw nothing that would cause the freeze. Can anyone suggest a cause?
Here is the memcmp implementation:
int memcmp(const void *cs, const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res = 0;
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
if ((res = *su1 - *su2) != 0)
break;
return res;
}
EDIT: The function causing the freeze is memcmp. When I commented it out, everything worked. Also, when I did I memcmp as follows
memcmp(write_buffer, write_buffer, 2048); //comparing two write_buffers
Everything worked as well. Only when I throw the cache_buffer into the mix is when I get the error. Also, above is a simplification of my actual code. Here is the entire function:
static int compare_data(sector_t location, struct bio * bio, struct cache_c * dmc)
{
struct dm_io_region where;
unsigned long bits;
int segno;
struct bio_vec * bvec;
struct page * page;
unsigned char * cache_data;
char * temp_data;
char * write_data;
int result, length, i;
cache_data = (unsigned char *)vmalloc((dmc->block_size * 512) + 1);
where.bdev = dmc->cache_dev->bdev;
where.count = dmc->block_size;
where.sector = location << dmc->block_shift;
printk(KERN_DEBUG "place: %llu\n", where.sector);
dm_io_sync_vm(1, &where, READ, cache_data, &bits, dmc);
length = 0;
bio_for_each_segment(bvec, bio, segno)
{
if(segno == 0)
{
page = bio_page(bio);
kmap(page);
write_data = (char *)page_address(page);
//kunmap(page);
length += bvec->bv_len;
}
else
{
page = bio_page(bio);
kmap(page);
temp_data = strcat(write_data, (char *)page_address(page));
//kunmap(page);
write_data = temp_data;
length += bvec->bv_len;
}
}
printk(KERN_INFO "length: %u\n", length);
cache_data[dmc->block_size * 512] = '\0';
for(i = 0; i < 2048; i++)
{
printk("%c", write_data[i]);
}
printk("\n");
for(i = 0; i < 2048; i++)
{
printk("%c", cache_data[i]);
}
printk("\n");
result = memcmp(write_data, cache_data, length);
return result;
}
EDIT #2: Sorry guys. The problem was not memcmp. It was the result of memcmp. When ever it returned a positive or negative number, the function that called my function would play with some pointers, one of which was uninitialized. I don't know why I didn't realize it before. Thanks for trying to help though!
I'm no kernel expert, but I would assume you need to keep this memory mapped while doing the comparison? In other words, don't call kunmap until after the memcmp is complete. I would presume that calling it before will result in write_buffer pointing to a page which is no longer mapped.
Taking your code in the other question, here is a rough attempt at incremental. Still needs some cleanup, I'm sure:
static int compare_data(sector_t location, struct bio * bio, struct cache_c * dmc)
{
struct dm_io_region where;
unsigned long bits;
int segno;
struct bio_vec * bvec;
struct page * page;
unsigned char * cache_data;
char * temp_data;
char * write_data;
int length, i;
int result = 0;
size_t position = 0;
size_t max_size = (dmc->block_size * 512) + 1;
cache_data = (unsigned char *)vmalloc(max_size);
where.bdev = dmc->cache_dev->bdev;
where.count = dmc->block_size;
where.sector = location << dmc->block_shift;
printk(KERN_DEBUG "place: %llu\n", where.sector);
dm_io_sync_vm(1, &where, READ, cache_data, &bits, dmc);
bio_for_each_segment(bvec, bio, segno)
{
// Map the page into memory
page = bio_page(bio);
write_data = (char *)kmap(page);
length = bvec->bv_len;
// Make sure we don't go past the end
if(position >= max_size)
break;
if(position + length > max_size)
length = max_size - position;
// Compare the data
result = memcmp(write_data, cache_data + position, length);
position += length;
kunmap(page);
// If the memory is not equal, bail out now and return the result
if(result != 0)
break;
}
cache_data[dmc->block_size * 512] = '\0';
return result;
}

Resources