Adding a field to msgbuf - linux

I got an assignment to create a message queue in Linux. I need to use msgnd() and msgrcv() functions. Everything works if my message structure has two fields mtype and mtext[] but I need to add one more field an int mpid. But when I read the value from mpid it is just garbage from the memory. I searched for answers or examples but I only found structures with two fields. Can I even add more?
struct myBuff{
long mtype;
char mtext[255];
int mpid;
};
code for the sender
void add_message(int id, struct myBuff buff){
int size = strlen(buff.mtext) + 1 + sizeof(int)
if (size > 255 + sizeof(int))
exit(EXIT_FAILURE);
msgsnd(id, (struct msgbuf*)&buff, size, 0 | MSG_NOERROR);
}
code for the receiver
void check_message(int id, struct myBuff* buff)
{
msgrcv(id, (struct msgbuf*)buff, 255 + sizeof(int), buff->mtype, 0 | MSG_NOERROR);
}

Related

Linux skb packet count header and metadata

Could not find any info about this header at the end of Skb and about this metadata
So it seems it is user controlled and should be checked for bounds
static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
struct sk_buff *ax_skb;
int pkt_cnt;
u32 rx_hdr;
u16 hdr_off;
u32 *pkt_hdr;
/* At the end of the SKB, there's a header telling us how many packets
* are bundled into this buffer and where we can find an array of
* per-packet metadata (which contains elements encoded into u16).
*/
if (skb->len < 4)
return 0;
skb_trim(skb, skb->len - 4);
rx_hdr = get_unaligned_le32(skb_tail_pointer(skb));
pkt_cnt = (u16)rx_hdr;
hdr_off = (u16)(rx_hdr >> 16);
if (pkt_cnt == 0)
return 0;
/* Make sure that the bounds of the metadata array are inside the SKB
* (and in front of the counter at the end).
*/
if (pkt_cnt * 2 + hdr_off > skb->len)
return 0;
Can somebody point to code in Kernel or references describing it?
USB Ethernet devices don't have hardware framing support, so have their own schemes to do framing by inserting extra bytes in the packet. The tx_fixup and rx_fixup are provided to do that. Some of the schemes are described here: http://www.linux-usb.org/usbnet

ebpf packet filter on payload matching

I am new in ebpf & xdp topic and want to do learn it. My question is how to use ebpf filter to filter the packet on specific payload matching? for example, if the data(payload) of the packet is 1234 its passes to the network stack otherwise it blocks the packet. I reached payload length. For example, if I want to match the message payload length it works fine but when I start matching the payload characters I got an error. here is my code:
int ret_val;
unsigned long payload_offset;
unsigned long payload_size;
const char *payload = "test";
struct ethhdr *eth = data;
if ((void*)eth + sizeof(*eth) <= data_end) {
struct iphdr *ip = data + sizeof(*eth);
if ((void*)ip + sizeof(*ip) <= data_end) {
if (ip->protocol == IPPROTO_UDP ) {
struct udphdr *udp = (void*)ip + sizeof(*ip);
if ((void*)udp + sizeof(*udp) <= data_end) {
if (udp->dest == ntohs(5005)) {
payload_offset = sizeof(struct udphdr);
payload_size = ntohs(udp->len) - sizeof(struct udphdr);
unsigned char *s = (unsigned char *)&payload_size;
if (ret_val == __builtin_memcmp(s,payload,4) == 0) {
return XDP_DROP;
}
}
}
}
}
}
The error had removed but unable to compare the payload... I am sending the UDP message from python socket code. If I compare the payload length it works fine.
What did you try? You should probably read a bit more about eBPF to try to understand how to process packets, the basic example you give does not sound too complicated.
Basically you would have to parse the headers to see where your payload begins. Simple BPF parsing examples might help you understand the principles:
Start from beginning of header (e.g. Ethernet at first)
Check packet is long enough to hold the header (or you would risk an out-of-bound access when trying to access the upper layers otherwise)
Add header length to get the offset of your next header (e.g. IPv4, then e.g. TCP...)
Rinse and repeat.
In your case you would process all headers until you get the offset of the data payload. Note that this is trivial if the traffic you try to match always has the same headers (e.g. always IPv4 and UDP), but you get more cases to sort out if there is a mix (IPv4 + IPv6, encapsulation, IPv4 options...).
Once you have the offset for your data, just compare data at this offset to your pattern (that you may hardcode in the BPF program or get from a BPF map, depending on your use case). Note that you do not have access to strcmp(), but __builtin_memcmp() is available if you need to compare more than 64 bits.
(All the above applying of course to a C program that you would compile into an object file containing eBPF instructions with the LLVM back-end.)
If you were to search for a string at an arbitrary offset in the payload, know that eBPF now supports (bounded) loops since kernel 5.3 (if I remember correctly).
Your edit is pretty much a new question, so here an updated answer. Please consider opening a new question instead in the future.
There are a number of things that are wrong in your program. In particular:
1| payload_offset = sizeof(struct udphdr);
2| payload_size = ntohs(udp->len) - sizeof(struct udphdr);
3| unsigned char *s = (unsigned char *)&payload_size;
4|
5| if (ret_val == __builtin_memcmp(s, payload, 4) == 0) {
6| return XDP_DROP;
7| }
On line 1, your payload_offset variable is not an offset, it just contains the length of the UDP header. You would need to add that to the start of the UDP header to get the actual payload offset.
Line 2 is fine.
Line 3 does not make any sense! You make s (that you later compare to your pattern) point towards the size of the payload? (a.k.a “I told you so in the comments! :)”). Instead, it should point to... the beginning of the payload, maybe? So, basically, data + payload_offset (once offset is fixed).
Between lines 3 and 5, the check on payload length is missing. When you try to access your payload in s (__builtin_memcmp(s, payload, 4)), you try to compare four bytes of packet data; you must ensure that the packet is long enough to read those four bytes (just as you checked the length each time before you read from an Ethernet, IP or UDP header field).
While at it, we can also check that the length of the payload is equal to the length of the pattern to match, and exit if they differ without having to compare the bytes.
Line 5 has a == instead of =, as discussed in the comments. Easy to fix. However, I had no luck with __builtin_memcmp() for your program, it seems LLVM does not want to inline it and turns it into a failing function call. Never mind, we can work without it. For your example, you can cast to int and compare the four-byte long values directly. For longer patterns, and for recent kernels (or by unrolling if pattern size is fixed), we can use bounded loops.
Here is a amended version of your program, that works on my setup.
#include <arpa/inet.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
int xdp_func(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
char match_pattern[] = "test";
unsigned int payload_size, i;
struct ethhdr *eth = data;
unsigned char *payload;
struct udphdr *udp;
struct iphdr *ip;
if ((void *)eth + sizeof(*eth) > data_end)
return XDP_PASS;
ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end)
return XDP_PASS;
if (ip->protocol != IPPROTO_UDP)
return XDP_PASS;
udp = (void *)ip + sizeof(*ip);
if ((void *)udp + sizeof(*udp) > data_end)
return XDP_PASS;
if (udp->dest != ntohs(5005))
return XDP_PASS;
payload_size = ntohs(udp->len) - sizeof(*udp);
// Here we use "size - 1" to account for the final '\0' in "test".
// This '\0' may or may not be in your payload, adjust if necessary.
if (payload_size != sizeof(match_pattern) - 1)
return XDP_PASS;
// Point to start of payload.
payload = (unsigned char *)udp + sizeof(*udp);
if ((void *)payload + payload_size > data_end)
return XDP_PASS;
// Compare each byte, exit if a difference is found.
for (i = 0; i < payload_size; i++)
if (payload[i] != match_pattern[i])
return XDP_PASS;
// Same payload, drop.
return XDP_DROP;
}

linux uinput: simple example?

I'm having some problems getting both sides of code using uinput working.
Based on Getting started with uinput: the user level input subsystem[dead link; archived] I put together the following writer (minus error handling):
int main(int ac, char **av)
{
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
int ret = ioctl(fd, UI_SET_EVBIT, EV_ABS);
ret = ioctl(fd, UI_SET_ABSBIT, ABS_X);
struct uinput_user_dev uidev = {0};
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-rotary");
uidev.absmin[ABS_X] = 0;
uidev.absmax[ABS_X] = 255;
ret = write(fd, &uidev, sizeof(uidev));
ret = ioctl(fd, UI_DEV_CREATE);
struct input_event ev = {0};
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = 42;
ret = write(fd, &ev, sizeof(ev));
getchar();
ret = ioctl(fd, UI_DEV_DESTROY);
return EXIT_SUCCESS;
}
That seems to work, at least the full input_event structure seems to be written.
I then wrote the most naive reader of events I could come up with:
int main(int ac, char **av)
{
int fd = open(av[1], O_RDONLY);
char name[256] = "unknown";
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
printf("reading from %s\n", name);
struct input_event ev = {0};
int ret = read(fd, &ev, sizeof(ev));
printf("Read an event! %i\n", ret);
printf("ev.time.tv_sec: %li\n", ev.time.tv_sec);
printf("ev.time.tv_usec: %li\n", ev.time.tv_usec);
printf("ev.type: %hi\n", ev.type);
printf("ev.code: %hi\n", ev.code);
printf("ev.value: %li\n", ev.value);
return EXIT_SUCCESS;
}
Unfortunately the reader side doesn't work at all; only manages to read 8 bytes each time, which isn't nearly a full input_event structure.
What silly mistake am I making?
You should also be writing a sync event after the actual event. In your writer side code:
struct input_event ev = {0};
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = 42;
usleep(1500);
memset(&ev, 0, sizeof(ev));
ev.type = EV_SYN;
ev.code = 0;
ev.value = 0;
ret = write(fd, &ev, sizeof(ev));
getchar();
TL;DR: The kernel expects an EV_SYN event of code SYN_REPORT because individual events may be grouped together, i.e., when they happen at the same point in time.
You can think of it as the kernel interpreting a group of events rather than individual events as specified in struct input_event. An EV_SYN event delimits these groups of events with SYN_REPORT as code, i.e., this provides a grouping mechanism for signaling events that occur simultaneously.
For example, imagine you touch with your finger on the surface of a touchpad:
Given the definition of struct input_event in linux/input.h:
struct input_event {
/* ... */
__u16 type; /* e.g., EV_ABS, EV_REL */
__u16 code; /* e.g., ABS_X for EV_ABS */
__s32 value; /* e.g., the value of the x coordinate for ABS_X */
};
It isn't possible to create a single EV_ABS event that can hold both the codes ABS_X and ABS_Y, as well as the values for specifying the x and y coordinates, respectively – there is just a single code member in struct input_event1.
Instead, two EV_ABS events2 will be created:
One with the code ABS_X and the x coordinate as value.
Another with the code ABS_Y and the y coordinate as value.
It wouldn't be entirely correct to interpret these two events as two events separated in time, i.e., one that indicates a change of the x coordinate and another that occurs later in time and indicates a change of the y coordinate.
Instead of two in-time-separated events, these events should be grouped together and interpreted by the kernel as a single unit of input data change that indicates a change of both the x and y coordinates at the same time. This is why the EV_SYN mechanism described above exists: by generating one EV_SYN event with the SYN_REPORT code just after these two EV_ABS events (i.e., ABS_X and ABS_Y), we are able to group them as a unit.
1This is directly related to the fact that a struct input_event is of fixed size and can't grow arbitrarily.
2Tecnically, more events may be created. For example, another event of type EV_KEY with the code BTN_TOUCH will likely be created. However, this is irrelevant for the point I want to make.

Is the sscanf function in the Linux kernel susceptible to buffer overflow attacks?

From what I understand, a typical buffer overflow attack occurs when an attack overflows a buffer of memory on the stack, thus allowing the attacker to inject malicious code and rewrite the return address on the stack to point to that code.
This is a common concern when using functions (such as sscanf) that blindly copy data from one area to another, checking one for a termination byte:
char str[8]; /* holds up to 8 bytes of data */
char *buf = "lots and lots of foobars"; /* way more than 8 bytes of data */
sscanf(buf, "%s", str); /* buffer overflow occurs here! */
I noticed some sysfs_ops store functions in the Linux kernel are implemented with the Linux kernel's version of the sscanf function:
static char str[8]; /* global string */
static ssize_t my_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
sscanf(buf, "%s", str); /* buf holds more than 8 bytes! */
return size;
}
Suppose this store callback function is set to a writable sysfs attribute. Would a malicious user be able to intentionally overflow the buffer via a write call?
Normally, I would expect guards against buffer overflow attacks -- such as limiting the number of bytes read -- but I see none in a good number of functions (for example in drivers/scsi/scsi_sysfs.c).
Does the implementation of the Linux kernel version of sscanf protect against buffer overflow attacks; or is there another reason -- perhaps buffer overflow attacks are impossible given how the Linux kernel works under the hood?
The Linux sscanf() is vulnerable to buffer overflows; inspection of the source shows this. You can use width specifiers to limit the amount a %s is allowed to write. At some point your str must have had copy_from_user() run on it as well. It is possible the user space to pass some garbage pointer to the kernel.
In the version of Linux you cited, the scsi_sysfs.c does have a buffer overflow. The latest version does not. The committed fix should fix the issue you see.
Short answer:
sscanf, when well called, will not cause buffer overflow, especially in sysfs xxx_store() function. (There are a lot sscanf in sysfs XXX_store() examples), because Linux kernel add a '\0' (zero-terminated) byte after the string (buf[len] = 0;) for your XXX_store() function.
Long answer:
Normally, sysfs are defined to have a strict formatted data. Since you expect 8 bytes at most, it's reasonable to limit the size you get like this:
static char str[8]; /* global string */
static ssize_t my_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
if (size > 8) {
printk("Error: Input size > 8: too large\n");
return -EINVAL;
}
sscanf(buf, "%s", str); /* buf holds more than 8 bytes! */
return size;
}
(Note: use 9 rather than 8, if you expect a 8-bytes string plus '\n')
(Note that you do reject some inputs such as those with many leading white spaces. However, who would send a string with many leading white spaces? Those who want to break your code, right? If they don't follow your spec, just reject them.)
Note that Linux kernel purposely inserts a '\0' at offset len (i.e. buf[len] = 0;) when the user write len bytes to sysfs purposely for safe sscanf, as said in a comment in kernel 2.6: fs/sysfs/file.c:
static int
fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t count)
{
int error;
if (!buffer->page)
buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
if (count >= PAGE_SIZE)
count = PAGE_SIZE - 1;
error = copy_from_user(buffer->page,buf,count);
buffer->needs_read_fill = 1;
/* if buf is assumed to contain a string, terminate it by \0,
so e.g. sscanf() can scan the string easily */
buffer->page[count] = 0;
return error ? -EFAULT : count;
}
...
static ssize_t
sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct sysfs_buffer * buffer = file->private_data;
ssize_t len;
mutex_lock(&buffer->mutex);
len = fill_write_buffer(buffer, buf, count);
if (len > 0)
len = flush_write_buffer(file->f_path.dentry, buffer, len);
if (len > 0)
*ppos += len;
mutex_unlock(&buffer->mutex);
return len;
}
Higher kernel version keeps the same logic (though already completely rewritten).

Passing struct to device driver through IOCTL

I am trying to pass a struct from user space to kernel space. I had been trying for many hours and it isn't working. Here is what I have done so far..
int device_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg){
int ret, SIZE;
switch(cmd){
case PASS_STRUCT_ARRAY_SIZE:
SIZE = (int *)arg;
if(ret < 0){
printk("Error in PASS_STRUCT_ARRAY_SIZE\n");
return -1;
}
printk("Struct Array Size : %d\n",SIZE);
break;
case PASS_STRUCT:
struct mesg{
int pIDs[SIZE];
int niceVal;
};
struct mesg data;
ret = copy_from_user(&data, arg, sizeof(*data));
if(ret < 0){
printk("PASS_STRUCT\n");
return -1;
}
printk("Message PASS_STRUCT : %d\n",data.niceVal);
break;
default :
return -ENOTTY;
}
return 0;
}
I have trouble defining the struct. What is the correct way to define it? I want to have int pIDs[SIZE]. Will int *pIDs do it(in user space it is defined like pIDs[SIZE])?
EDIT:
With the above change I get this error? error: expected expression before 'struct' any ideas?
There are two variants of the structure in your question.
struct mesg1{
int *pIDs;
int niceVal;
};
struct mesg2{
int pIDs[SIZE];
int niceVal;
};
They are different; in case of mesg1 you has pointer to int array (which is outside the struct). In other case (mesg2) there is int array inside the struct.
If your SIZE is fixed (in API of your module; the same value used in user- and kernel- space), you can use second variant (mesg2).
To use first variant of structure (mesg1) you may add field size to the structure itself, like:
struct mesg1{
int pIDs_size;
int *pIDs;
int niceVal;
};
and fill it with count of ints, pointed by *pIDs.
PS: And please, never use structures with variable-sized arrays in the middle of the struct (aka VLAIS). This is proprietary, wierd, buggy and non-documented extension to C language by GCC compiler. Only last field of struct can be array with variable size (VLA) according to international C standard. Some examples here: 1 2
PPS:
You can declare you struct with VLA (if there is only single array with variable size):
struct mesg2{
int niceVal;
int pIDs[];
};
but you should be careful when allocating memory for such struct with VLA

Resources