Writing data to file but file still contain NULL - c

Related to my previous post but its not a duplicate of that.Now I have tried something and
Here I am asking you about the logical error in code.
/*u_int8_t ....etc are alias for uint8_t...etc so don't bother about them*/
void crypt(u_int8_t *key, u_int32_t keylen,
u_int8_t *data, u_int32_t datalen)
{
FILE *fp,*fq;
fp=fopen("key","w");
fputs((char *)key,fp);
fq=fopen("file.txt","w");
d=0;
while(data[d]) {
fputc((int)data[d],fq);
d++;
}
fputc('\0',fq);
fclose(fp);
fclose(fq)
}
Output :
udit#udit-Dabba ~/Downloads/sendip-2.5-mec-2/mec $ cat key
kaheudit#udit-Dabba ~/Downloads/sendip-2.5-mec-2/mec $ cat file.txt
udit#udit-Dabba ~/Downloads/sendip-2.5-mec-2/mec $
Key gets printed to file but not the data.
Now when I slightly modify the code :
void
crypt(u_int8_t *key, u_int32_t keylen,
u_int8_t *data, u_int32_t datalen)
{
int d,k;
FILE *fp,*fq;
fp=fopen("key","w");
fputs((char *)key,fp);
fq=fopen("file.txt","w");
for (d=0, k=0; d < datalen; ++d, k = (k+1)%keylen) {
data[d] ^= key[k];
fputc(data[d],fq);
}
fclose(fp);
fclose(fq);
}
Now key as well as data gets printed...although data is not exactly correct(but it is able to be written down into the file)
udit#udit-Dabba ~/Downloads/sendip-2.5-mec-2/mec $ cat key
kaheudit#udit-Dabba ~/Downloads/sendip-2.5-mec-2/mec $ cat file.txt
kthpOWWkahe;c��"�he
kajcudit#udit-Dabba ~/Downloads/sendip-2.5-mec-2/mec $
The call to the crypt function is as follows -
bool
espcrypto(esp_private *epriv, sendip_data *data, sendip_data *pack)
{
u_int32_t keylen;
u_int8_t *key;
static u_int8_t fakekey;
struct ip_esp_hdr *esp = (struct ip_esp_hdr *)pack->data;
if (!epriv->keylen) { /* This isn't going to be very productive... */
key = &fakekey;
keylen = 1;
} else {
key = (u_int8_t *)epriv->key;
keylen = epriv->keylen;
}
/* Encrypt everything past the ESP header */
crypt(key, keylen,
(u_int8_t *)esp->enc_data,
pack->alloc_len + data->alloc_len
- sizeof(struct ip_esp_hdr));
return TRUE;
}
The following packet describe what data I actually need to write down to the file...
udit#udit-Dabba ~/Downloads/sendip-2.5-mec-2/mec $ sendip -v -p ipv6 -dabcd -6s ::1 -p
esp -es 0x20 -eq 0x40 -ek "kahe" -ec crypt.so -p tcp -ts 21 -td 21 ::2
Added 43 options
Initializing module ipv6
Initializing module esp
Initializing module tcp
Finalizing module tcp
Finalizing module esp
Finalizing module ipv6
Final packet data:
60 00 00 00 `...
00 24 32 20 .$2
00 00 00 00 ....
00 00 00 00 ....
00 00 00 00 ....
00 00 00 01 ....
00 00 00 00 ....
00 00 00 00 ....
00 00 00 00 ....
00 00 00 02 ....
00 00 00 20 ...
00 00 00 40 ...#
6B 74 68 70 kthp /*data portion starts from here*/
4F 57 1F 57 OW.W
6B 61 68 65 kahe
3B 63 97 9A ;c..
22 C0 68 65 ".he
0A 03 0B 01 ....
6B 61 6A 63 kajc /*data portion ends here*/
Freeing module ipv6
Freeing module esp
Freeing module tcp
Please help me.... I haven't receiver any satisfactory implementation on my previous post still so trying my own hand.Really need it ..

You're using string semantics to handle binary data. That's not going to work. If you look closely you'll see that the first character in your file.txt example output is k, which is also the first character of the key. That means your data is starting with a NUL byte and the while loop will instantly exit.
First of all you need to open the files in binary mode:
fp=fopen("key","wb");
fq=fopen("file.txt","wb");
To write the key use
fwrite(key, keylen, 1, fp);
and then use the for loop in your second example to write the data. I can't see anything wrong with that one, your problem might simply have been the binary vs text mode.
Edit: Try using hexdump -C file.txt to view your file instead of cat.

Here
while(data[d]) {
fputc((int)data[d],fq);
d++;
}
data[d] is 0 (as it is binary data), so it will leave the loop "too" soon.

Related

bpf_trace_printk causes program not loaded in kernel -- libbpf: Program 'xdp' contains unrecognized relo data pointing to section 6

following is my code and libbpf throws error that
libbpf: Program 'xdp' contains unrecognized relo data pointing to section 6
ERR: loading BPF-OBJ file(./k.o) (-2): No such file or directory
ERR: loading file: ./k.o
Why is that. This is how I am printing with bpf_trace_printk
bpf_trace_printk("hello\n",sizeof("hello\n"));
this is full code
#include <stddef.h>
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
/* Defines xdp_stats_map from packet04 */
#include "../common/xdp_stats_kern_user.h"
#include "../common/xdp_stats_kern.h"
#include <../common/parsing_helpers.h>
#include <bpf/bpf_helpers.h>
#define ETH_ALEN 6
#define MAX_ENTRIES 1000
/*struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, MAX_ENTRIES);
} hash_map SEC(".maps");
*/
struct hash_elem {
int cnt;
struct bpf_spin_lock lock;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
__type(value, struct hash_elem);
__uint(max_entries, 100);
} hash_map SEC(".maps");
struct a{struct bpf_spin_lock lock;};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
__type(value, long);
__uint(max_entries, 2);
} hash_map1 SEC(".maps");
//static __u32 i=0;
struct icmphdr1
{
__u8 type; /* message type */
__u8 code; /* type sub-code */
__u16 checksum;
union
{
struct
{
__u16 id;
__u16 sequence;
} echo; /* echo datagram */
__u32 gateway; /* gateway address */
struct
{
__u16 __glibc_reserved;
__u16 mtu;
} frag; /* path mtu discovery */
} un;
};
SEC("xdp")
int xdp_prog_simple(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
__u16 h_proto;
if (eth + 1 > data_end)
return XDP_DROP;
h_proto = eth->h_proto;
if (h_proto == bpf_htons(ETH_P_IP))
{
struct iphdr *ip=(struct iphdr *)(eth+sizeof(struct ethhdr));
if(ip+1>data_end)
{
__u8 protocol_ip=ip->protocol;
if(protocol_ip==0x01)
{
struct icmphdr1 *icmp=(struct icmphdr1 *)(ip+sizeof(struct iphdr));
if(icmp+1>data_end)
{
__u8 type=icmp->type;
__u8 code=icmp->code;
if(type!=10 && code!=11)
bpf_trace_printk("hello\n",sizeof("hello\n"));
// if(type==8)
// if(code==0)
// bpf_trace_printk("code is 0\n",sizeof("code is 0\n"));
// bpf_trace_printk("icmp type:code = %d:%d\n",sizeof("icmp type:code = %d:%d\n"),type,code);
}
//__u32 key=i;
//__u64 val=bpf_ktime_get_ns();
//int x=bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
}
}
}
return XDP_DROP;
}
TL;DR
Do not call your helper like this:
// BAD
bpf_trace_printk("hello\n", sizeof("hello\n"));
or like this:
// BAD
const char *msg = "hello\n";
bpf_trace_printk(msg, sizeof("hello\n"));
But instead, declare your string as a dynamic array of characters:
// GOOD
char msg[] = "hello\n";
bpf_trace_printk(msg, sizeof(msg));
This will prevent clang from creating a relocation that libbpf cannot handle.
Explanations
Let's have a look at the object file, when passing the string directly:
#include <linux/bpf.h>
#include "bpf_helper_defs.h"
int foo(void)
{
bpf_trace_printk("hello\n", sizeof("hello\n"));
return 0;
}
When doing this, clang puts the string into a read-only section, and requests a relocation. We can observe this with llvm-objdump. Let's inspect the relocations and disassemble the program:
$ clang -O2 -emit-llvm -c foo.c -o - | llc -march=bpf -filetype=obj -o foo.
$ llvm-objdump -r foo.o
foo.o: file format elf64-bpf
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000000000000 R_BPF_64_64 .rodata.str1.1
RELOCATION RECORDS FOR [.eh_frame]:
OFFSET TYPE VALUE
000000000000001c R_BPF_64_ABS64 .text
$ lvm-objdump --section=.text -D foo.o
foo.o: file format elf64-bpf
Disassembly of section .text:
0000000000000000 <foo>:
0: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
2: b7 02 00 00 07 00 00 00 r2 = 7
3: 85 00 00 00 06 00 00 00 call 6
4: b7 00 00 00 00 00 00 00 r0 = 0
5: 95 00 00 00 00 00 00 00 exit
We note that the .text section, containing the program, starts with a single load r1 = 0: the register r1, containing the first argument to pass to the call to bpf_trace_printk() (call 6), is not set until this relocation happens.
But libbpf does not support this kind of relocations, this is why you get your error message:
Program 'xdp' contains unrecognized relo data pointing to section 6
The same can be observed with:
#include <linux/bpf.h>
#include "bpf_helper_defs.h"
int foo(void)
{
const char* msg = "hello\n";
bpf_trace_printk(msg, sizeof("hello\n"));
return 0;
}
This is equivalent, clang creates a relocation too.
However, we can instead declare the string as a dynamic array of characters:
#include <linux/bpf.h>
#include "bpf_helper_defs.h"
int foo(void)
{
char msg[] = "hello\n";
bpf_trace_printk(msg, sizeof("hello\n"));
return 0;
}
In that case, the array goes to the stack. No relocation happens. The .rodata.str1.1 section is not present in the file. We can check what llvm-objdump says:
$ clang -O2 -emit-llvm -c foo.c -o - | llc -march=bpf -filetype=obj -o foo.o
$ llvm-objdump -r foo.o
foo.o: file format elf64-bpf
RELOCATION RECORDS FOR [.eh_frame]:
OFFSET TYPE VALUE
000000000000001c R_BPF_64_ABS64 .text
$ lvm-objdump --section=.text -D foo.o
foo.o: file format elf64-bpf
Disassembly of section .text:
0000000000000000 <foo>:
0: b7 01 00 00 6f 0a 00 00 r1 = 2671
1: 6b 1a fc ff 00 00 00 00 *(u16 *)(r10 - 4) = r1
2: b7 01 00 00 68 65 6c 6c r1 = 1819043176
3: 63 1a f8 ff 00 00 00 00 *(u32 *)(r10 - 8) = r1
4: b7 01 00 00 00 00 00 00 r1 = 0
5: 73 1a fe ff 00 00 00 00 *(u8 *)(r10 - 2) = r1
6: bf a1 00 00 00 00 00 00 r1 = r10
7: 07 01 00 00 f8 ff ff ff r1 += -8
8: b7 02 00 00 07 00 00 00 r2 = 7
9: 85 00 00 00 06 00 00 00 call 6
10: b7 00 00 00 00 00 00 00 r0 = 0
11: 95 00 00 00 00 00 00 00 exit
Here, we fill the stack (r10 is the stack pointer) with the characters of the string (68 65 6c 6c 6f 0a 00 00 is hello\n\0\0). Everything is processed in BPF, there is no relocation involved. And this works just fine.
Could we do better? Yes, with Linux 5.2 and older, we can avoid having the array on the stack by declaring the string as:
static const char msg[] = "hello\n";
This time, this results in a relocation to section .rodata, but one that libbpf does handle, through the support of static variables. More details are available here.
Generally speaking, BPF tips & tricks: the guide to bpf_trace_printk() and bpf_printk() is an excellent reference on the bpf_trace_printk() helper.

Lock a certain range in a file using `fcntl` and `F_OFD_SETLK`

Objective
I want to open a file multiple times, but each fd should be only allowed to write to a specific range.
Background
I have an EEPROM which contains multiple "partitions", where each partition holds different information.
I want to avoid that one partition overflows to a different one, or one partition can read other information and misinterpret them.
My Problem
I wanted to use fcntl along with F_OFD_SETLK so that I can lock a specific range on each opened fd.
Locking works as intended and trying to lock an already locked range will result in EAGAIN, which is expected.
What is not so obvious for me is, that I can write to a range that is locked by a different fd.
Question
Is it possible to lock a certain range in a file so that it is not writeable by a different opened fd?
If not, is there a different way to achieve my goal?
Code:
Link to onlinegdb: https://onlinegdb.com/ewE767rbu
#define _GNU_SOURCE 1
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void ex(const char *what)
{
perror(what);
exit(1);
}
static void lock_range(int fd, off_t start, off_t len)
{
struct flock ff = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = start,
.l_len = len,
};
if (fcntl(fd, F_OFD_SETLK, &ff) < 0)
perror("fcntl");
}
static void write_at(int fd, const char *str, off_t offset)
{
if (pwrite(fd, str, strlen(str), offset) < 0)
perror("pwrite");
}
int main()
{
int firstfd = open("/tmp/abc.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if (firstfd < 0)
ex("open");
if (ftruncate(firstfd, 0x1000) < 0)
ex("ftruncate");
lock_range(firstfd, 0, 0x800);
lock_range(firstfd, 0, 0x800); // check if I can aquire the lock multiple times
int secondfd = open("/tmp/abc.txt", O_RDWR);
if (secondfd < 0)
ex("open");
lock_range(secondfd, 0, 0x800); // this one fails on purpose
lock_range(secondfd, 0x800, 0);
lock_range(firstfd, 0x801, 1); // and this one fails on purpose
write_at(firstfd, "hallo", 0);
write_at(firstfd, "hallo", 0x900); // this should fail, but doesn't
write_at(secondfd, "welt", 0); // this should fail, but doesn't
write_at(secondfd, "welt", 0x900);
close(firstfd);
close(secondfd);
system("hexdump -C /tmp/abc.txt"); // just for visualization
}
Output:
fcntl: Resource temporarily unavailable
fcntl: Resource temporarily unavailable
00000000 77 65 6c 74 6f 00 00 00 00 00 00 00 00 00 00 00 |welto...........|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000900 77 65 6c 74 6f 00 00 00 00 00 00 00 00 00 00 00 |welto...........|
00000910 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000
Please note welto which is hallo overriden by welt. I expected hallo at 0x0 and welt at 0x900.
Locks come in two flavours: mandatory locks, and advisory locks. These are advisory locks. This means they prevent others from obtaining a lock. Period. They don't prevent writes or any other form of modification.
If not, is there a different way to achieve my goal?
Don't ignore failures to obtain a lock.

Unable to use skb->data with SO_ATTACH_BPF but working with tc [duplicate]

I'm trying to write a simple socket filter eBPF program that can access the socket buffer data.
#include <linux/bpf.h>
#include <linux/if_ether.h>
#define SEC(NAME) __attribute__((section(NAME), used))
SEC("socket_filter")
int myprog(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data;
if ((void*)eth + sizeof(*eth) > data_end)
return 0;
return 1;
}
And I'm compiling using clang:
clang -I./ -I/usr/include/x86_64-linux-gnu/asm \
-I/usr/include/x86_64-linux-gnu/ -O2 -target bpf -c test.c -o test.elf
However when I try to load the program I get the following verifier error:
invalid bpf_context access off=80 size=4
My understanding of this error is that it should be thrown when you try to access context data that hasn't been checked to be within data_end, however my code does do that:
Here is the instructions for my program
0000000000000000 packet_counter:
0: 61 12 50 00 00 00 00 00 r2 = *(u32 *)(r1 + 80)
1: 61 11 4c 00 00 00 00 00 r1 = *(u32 *)(r1 + 76)
2: 07 01 00 00 0e 00 00 00 r1 += 14
3: b7 00 00 00 01 00 00 00 r0 = 1
4: 3d 12 01 00 00 00 00 00 if r2 >= r1 goto +1 <LBB0_2>
5: b7 00 00 00 00 00 00 00 r0 = 0
which would imply that the error is being caused by reading the pointer to data_end? However it only happens if I don't try to check the bounds later.
This is because your BPF program is a “socket filter”, and that such programs are not allowed to do direct packet access (see sk_filter_is_valid_access(), where we return false on trying to read skb->data or skb->data_end for example). I do not know the specific reason why it is not available, although I suspect this would be a security precaution as socket filter programs may be available to unprivileged users.
Your program loads just fine as a TC classifier, for example (bpftool prog load foo.o /sys/fs/bpf/foo type classifier -- By the way thanks for the standalone working reproducer, much appreciated!).
If you want to access data for a socket filter, you can still use the bpf_skb_load_bytes() (or bpf_skb_store_bytes()) helper, which automatically does the check on length. Something like this:
#include <linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
static void *(*bpf_skb_load_bytes)(const struct __sk_buff *, __u32,
void *, __u32) =
(void *) BPF_FUNC_skb_load_bytes;
SEC("socket_filter")
int myprog(struct __sk_buff *skb)
{
__u32 foo;
if (bpf_skb_load_bytes(skb, 0, &foo, sizeof(foo)))
return 0;
if (foo == 3)
return 0;
return 1;
}
Regarding your last comment:
However it only happens if I don't try to check the bounds later.
I suspect clang compiles out the assignments for data and data_end if you do not use them in your code, so they are no longer present and no longer a problem for the verifier.

Compare 2 GDB-Core Dumps

i'm in serious trouble with a heap/stack corruption. To be able to set a data breakpoint and find the root of the problem, i want to take two core dumps using gdb and then compare them.
First one when i think the heap and stack are still ok, and a second one shortly before my program crashes.
How can i compare those dumps?
Information about my project:
using gcc 5.x
Plugin for a legacy, 3rd-party-program with RT-support. No sources available for the project (for me).
Legacy Project is C, My Plugin is C++.
Other things i tried:
Using address sanitizers -> won't work because the legacy program wont start with them.
Using undefined behavior sanitizers -> same
Figuring out what memory gets corrupted for data breakpoint -> no success, because the corrupted memory does not belong to my code.
Ran Valgrind -> no errors around my code.
Thank you for your help
Independent from your underlying motivation, I'd like to get into your question. You ask how the difference between two core dumps can be identified. This is going to be lengthy, but will hopefully give you your answer.
A core dump is represented by an ELF file that contains metadata and a specific set of memory regions (on Linux, this can be controlled via /proc/[pid]/coredump_filter) that were mapped into the given process at the time of dump creation.
The obvious way to compare the dumps would be to compare a hex-representation:
$ diff -u <(hexdump -C dump1) <(hexdump -C dump2)
--- /dev/fd/63 2020-05-17 10:01:40.370524170 +0000
+++ /dev/fd/62 2020-05-17 10:01:40.370524170 +0000
## -90,8 +90,9 ##
000005e0 00 00 00 00 00 00 00 00 00 00 00 00 80 1f 00 00 |................|
000005f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
The result is rarely useful because you're missing the context. More specifically, there's no straightforward way to get from the offset of a value change in the file to the offset corresponding to the process virtual memory address space.
So, more context if needed. The optimal output would be a list of VM addresses including before and after values.
Before we can get on that, we need a test scenario that loosely resembles yours. The following application includes a use-after-free memory issue that does not lead to a segmentation fault at first (a new allocation with the same size hides the issue). The idea here is to create a core dump using gdb (generate) during each phase based on break points triggered by the code:
dump1: Correct state
dump2: Incorrect state, no segmentation fault
dump3: Segmentation fault
The code:
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
int **g_state;
int main()
{
int value = 1;
g_state = malloc(sizeof(int*));
*g_state = &value;
if (g_state && *g_state) {
printf("state: %d\n", **g_state);
}
printf("no corruption\n");
raise(SIGTRAP);
free(g_state);
char **unrelated = malloc(sizeof(int*));
*unrelated = "val";
if (g_state && *g_state) {
printf("state: %d\n", **g_state);
}
printf("use-after-free hidden by new allocation (invalid value)\n");
raise(SIGTRAP);
printf("use-after-free (segfault)\n");
free(unrelated);
int *unrelated2 = malloc(sizeof(intptr_t));
*unrelated2 = 1;
if (g_state && *g_state) {
printf("state: %d\n", **g_state);
}
return 0;
}
Now, the dumps can be generated:
Starting program: test
state: 1
no corruption
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff7a488df in raise () from /lib64/libc.so.6
(gdb) generate dump1
Saved corefile dump1
(gdb) cont
Continuing.
state: 7102838
use-after-free hidden by new allocation (invalid value)
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff7a488df in raise () from /lib64/libc.so.6
(gdb) generate dump2
Saved corefile dump2
(gdb) cont
Continuing.
use-after-free (segfault)
Program received signal SIGSEGV, Segmentation fault.
main () at test.c:31
31 printf("state: %d\n", **g_state);
(gdb) generate dump3
Saved corefile dump3
A quick manual inspection shows the relevant differences:
# dump1
(gdb) print g_state
$1 = (int **) 0x602260
(gdb) print *g_state
$2 = (int *) 0x7fffffffe2bc
# dump2
(gdb) print g_state
$1 = (int **) 0x602260
(gdb) print *g_state
$2 = (int *) 0x4008c1
# dump3
$2 = (int **) 0x602260
(gdb) print *g_state
$3 = (int *) 0x1
Based on that output, we can clearly see that *g_state changed but is still a valid pointer in dump2. In dump3, the pointer becomes invalid. Of course, we'd like to automate this comparison.
Knowing that a core dump is an ELF file, we can simply parse it and generate a diff ourselves. What we'll do:
Open a dump
Identify PROGBITS sections of the dump
Remember the data and address information
Repeat the process with the second dump
Compare the two data sets and print the diff
Based on elf.h, it's relatively easy to parse ELF files. I created a sample implementation that compares two dumps and prints a diff that is similar to comparing two hexdump outputs using diff. The sample makes some assumptions (x86_64, mappings either match in terms of address and size or they only exist in dump1 or dump2), omits most error handling and always chooses a simple implementation approach for the sake of brevity.
#include <elf.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#define MAX_MAPPINGS 1024
struct dump
{
char *base;
Elf64_Shdr *mappings[MAX_MAPPINGS];
};
unsigned readdump(const char *path, struct dump *dump)
{
unsigned count = 0;
int fd = open(path, O_RDONLY);
if (fd != -1) {
struct stat stat;
fstat(fd, &stat);
dump->base = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
Elf64_Ehdr *header = (Elf64_Ehdr *)dump->base;
Elf64_Shdr *secs = (Elf64_Shdr*)(dump->base+header->e_shoff);
for (unsigned secinx = 0; secinx < header->e_shnum; secinx++) {
if (secs[secinx].sh_type == SHT_PROGBITS) {
if (count == MAX_MAPPINGS) {
count = 0;
break;
}
dump->mappings[count] = &secs[secinx];
count++;
}
}
dump->mappings[count] = NULL;
}
return count;
}
#define DIFFWINDOW 16
void printsection(struct dump *dump, Elf64_Shdr *sec, const char mode,
unsigned offset, unsigned sizelimit)
{
unsigned char *data = (unsigned char *)(dump->base+sec->sh_offset);
uintptr_t addr = sec->sh_addr+offset;
unsigned size = sec->sh_size;
data += offset;
if (sizelimit) {
size = sizelimit;
}
unsigned start = 0;
for (unsigned i = 0; i < size; i++) {
if (i%DIFFWINDOW == 0) {
printf("%c%016x ", mode, addr+i);
start = i;
}
printf(" %02x", data[i]);
if ((i+1)%DIFFWINDOW == 0 || i + 1 == size) {
printf(" [");
for (unsigned j = start; j <= i; j++) {
putchar((data[j] >= 32 && data[j] < 127)?data[j]:'.');
}
printf("]\n");
}
addr++;
}
}
void printdiff(struct dump *dump1, Elf64_Shdr *sec1,
struct dump *dump2, Elf64_Shdr *sec2)
{
unsigned char *data1 = (unsigned char *)(dump1->base+sec1->sh_offset);
unsigned char *data2 = (unsigned char *)(dump2->base+sec2->sh_offset);
unsigned difffound = 0;
unsigned start = 0;
for (unsigned i = 0; i < sec1->sh_size; i++) {
if (i%DIFFWINDOW == 0) {
start = i;
difffound = 0;
}
if (!difffound && data1[i] != data2[i]) {
difffound = 1;
}
if ((i+1)%DIFFWINDOW == 0 || i + 1 == sec1->sh_size) {
if (difffound) {
printsection(dump1, sec1, '-', start, DIFFWINDOW);
printsection(dump2, sec2, '+', start, DIFFWINDOW);
}
}
}
}
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "Usage: compare DUMP1 DUMP2\n");
return 1;
}
struct dump dump1;
struct dump dump2;
if (readdump(argv[1], &dump1) == 0 ||
readdump(argv[2], &dump2) == 0) {
fprintf(stderr, "Failed to read dumps\n");
return 1;
}
unsigned sinx1 = 0;
unsigned sinx2 = 0;
while (dump1.mappings[sinx1] || dump2.mappings[sinx2]) {
Elf64_Shdr *sec1 = dump1.mappings[sinx1];
Elf64_Shdr *sec2 = dump2.mappings[sinx2];
if (sec1 && sec2) {
if (sec1->sh_addr == sec2->sh_addr) {
// in both
printdiff(&dump1, sec1, &dump2, sec2);
sinx1++;
sinx2++;
}
else if (sec1->sh_addr < sec2->sh_addr) {
// in 1, not 2
printsection(&dump1, sec1, '-', 0, 0);
sinx1++;
}
else {
// in 2, not 1
printsection(&dump2, sec2, '+', 0, 0);
sinx2++;
}
}
else if (sec1) {
// in 1, not 2
printsection(&dump1, sec1, '-', 0, 0);
sinx1++;
}
else {
// in 2, not 1
printsection(&dump2, sec2, '+', 0, 0);
sinx2++;
}
}
return 0;
}
With the sample implementation, we can re-evaluate our scenario above. A except from the first diff:
$ ./compare dump1 dump2
-0000000000601020 86 05 40 00 00 00 00 00 50 3e a8 f7 ff 7f 00 00 [..#.....P>......]
+0000000000601020 00 6f a9 f7 ff 7f 00 00 50 3e a8 f7 ff 7f 00 00 [.o......P>......]
-0000000000602260 bc e2 ff ff ff 7f 00 00 00 00 00 00 00 00 00 00 [................]
+0000000000602260 c1 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00 [..#.............]
-0000000000602280 6e 6f 20 63 6f 72 72 75 70 74 69 6f 6e 0a 00 00 [no corruption...]
+0000000000602280 75 73 65 2d 61 66 74 65 72 2d 66 72 65 65 20 68 [use-after-free h]
-0000000000602290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
+0000000000602290 69 64 64 65 6e 20 62 79 20 6e 65 77 20 61 6c 6c [idden by new all]
The diff shows that *gstate (address 0x602260) was changed from 0x7fffffffe2bc to 0x4008c1:
-0000000000602260 bc e2 ff ff ff 7f 00 00 00 00 00 00 00 00 00 00 [................]
+0000000000602260 c1 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00 [..#.............]
The second diff with only the relevant offset:
$ ./compare dump1 dump2
-0000000000602260 c1 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00 [..#.............]
+0000000000602260 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
The diff shows that *gstate (address 0x602260) was changed from 0x4008c1 to 0x1.
There you have it, a core dump diff. Now, whether or not that can prove to be useful in your scenario depends on various factors, one being the timeframe between the two dumps and the activity that takes place within that window. A large diff will possibly be difficult to analyze, so the aim must be to minimize its size by choosing the diff window carefully.
The more context you have, the easier the analysis will turn out to be. For example, the relevant scope of the diff could be reduced by limiting the diff to addresses of the .data and .bss sections of the library in question if changes in there are relevant to your situation.
Another approach to reduce the scope: excluding changes to memory that is not referenced by the library. The relationship between arbitrary heap allocations and specific libraries is not immediately apparent. Based on the the addresses of changes in your initial diff, you could search for pointers in the .data and .bss sections of the library right in the diff implementation. This does not take every possible reference into account (most notably indirect references from other allocations, register and stack references of library-owned threads), but it's a start.

How to write file at specific position?

I made a function in C to create a file using Unix command dd:
dd if=/dev/zero of=file.data bs=8 count=32
This works and created file.data with size 256 bytes and if I open the file I can see it's empty.
Now I want to write to a specific position in this file using fseek and fwrite, but whenever I try to write to a position different from 0, it does nothing.
For example, If I want to write to position 2, I must also write to position 0 and 1.
void createFile() {
char command[100];
sprintf(comando, "dd if=/dev/zero of=file.data bs=8 count=32");
system(command);
}
void writeFile(int position, char * data) {
FILE * file = fopen("file.data", "r+");
fseek(file, position, SEEK_SET);
fwrite(data, strlen(data), 1, file);
fclose(file);
}
Some examples
Input:
writeFile(0, "0");
writeFile(1, "1");
writeFile(2, "2");
output > 012
Input:
writeFile(2, "2");
writeFile(1, "1");
writeFile(0, "0");
output > 012
Input:
writeFile(1, "1");
writeFile(2, "2");
output > empty
Is there some way to write into the file without having to write into the previous positions also?
You don't have to do anything special. Your code works, as long as you know how to demonstrate that it works. Here's a mildly extended version of it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void createFile(void)
{
char command[100];
sprintf(command, "dd if=/dev/zero of=file.data bs=8 count=32"); // Typo fixed!
system(command);
}
static void writeFile(int position, char *data)
{
FILE *file = fopen("file.data", "r+");
fseek(file, position, SEEK_SET);
fwrite(data, strlen(data), 1, file);
fclose(file);
}
int main(void)
{
createFile();
system("odx file.data");
writeFile(2, "012");
system("odx file.data");
return 0;
}
The odx command is a hex dump program; you could use od -c or xxd -g1 instead.
The sample output is:
32+0 records in
32+0 records out
256 bytes transferred in 0.000109 secs (2349544 bytes/sec)
0x0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* (15)
0x0100:
0x0000: 00 00 30 31 32 00 00 00 00 00 00 00 00 00 00 00 ..012...........
0x0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* (14)
0x0100:
The first three lines are from dd. I'm not convinced that using dd is necessary, but it does no great harm. The next three lines indicate that the first 16 bytes in the file are all zero bytes, and that this pattern repeats for 15 more lines, and then you reach EOF at offset 0x100 (25610). The next four lines show that there are 2 null bytes, then the three digits 012, then all null bytes to the end of file.

Resources