Skip to content

Commit

Permalink
encoding, core: Add findwatchpoint() and encoding
Browse files Browse the repository at this point in the history
Add the implementation of find_watchpoint().

Since the 64-bit architecture, the virtual address only uses the least
57 bit. So we can let the watchpoint store the information, like
operation type and object size, on the 64 to 57 bit.

Signed-off-by: Chih-En Lin <shiyn.lin@gmail.com>
  • Loading branch information
linD026 committed Jun 6, 2022
1 parent 462b396 commit b3bb2d3
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
PWD := $(CURDIR)

nr_cpu=1
# The number of watchpoint slot
nr_wp=64

INC=$(PWD)/include
INC_PARAMS=$(INC:%=-I%)
Expand All @@ -11,6 +13,7 @@ CFLAGS+=-O1
# Position Independent Code suitable for use in a shared library.
CFLAGS+=-fPIC
CFLAGS+=-D'UCSAN_NR_CPU=$(nr_cpu)'
CFLAGS+=-D'UCSAN_NR_WATCHPOINT'=$(nr_wp)

ifeq ($(CC), gcc)
CFLAGS+=-D'CONFIG_GCC=y'
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ $ make clean # Delete generated files
### Makefile Parameter

* `nr_cpu` : number of cpu.
* `nr_wp` : number of watchpoint slot.
* `CC` : compiler, gcc or clang.

## How to use?
Expand Down
25 changes: 25 additions & 0 deletions include/ucsan/encoding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef __UCSAN_ENCODING_H__
#define __UCSAN_ENCODING_H__

/* For 64-bit architecture, the sizeof(long) is 8 bytes */
#define BITS_PER_LONG 64

/*
* currently for 64-bit architecture, the max of address will be 57 bits from 5
* level page table of virtual address.
* So we can use the higher than 56 bit of address to specific the following:
*
* - tasn read/write operation : 1 bit
* - object size : 5 bits for 1,2,4,8,16 size
* - consumed : 1 bit
*/
#define WATCHPOINT_ADDR_MASK ((1UL << 56) - 1)
#define WATCHPOINT_CONSUMED_MASK (1UL << 56)
#define WATCHPOINT_SIZE_1 (1UL << 57)
#define WATCHPOINT_SIZE_2 (1UL << 58)
#define WATCHPOINT_SIZE_4 (1UL << 59)
#define WATCHPOINT_SIZE_8 (1UL << 60)
#define WATCHPOINT_SIZE_16 (1UL << 61)
#define WATCHPOINT_WRITE_MASK (1UL << 62)

#endif /* __UCSAN_ENCODING_H__ */
96 changes: 96 additions & 0 deletions src/core.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,111 @@
#define _GNU_SOURCE
#include <ucsan/compiler.h>
#include <ucsan/encoding.h>
#include <uapi/ucsan.h>
#include <stdio.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdlib.h>

#define UCSAN_ACCESS_READ 0x0
#define UCSAN_ACCESS_WRITE 0x1
#define UCSAN_ACCESS_COMPOUND 0x2

/* In Linux, the page size is 4096 bytes. */
#define PAGE_SIZE 4096
#define PAGE_SHIFT 12

/*
* We first use the page size and address to get the slot of the address reside.
* Then search all the index of that slot to get the corresponding watchpoint.
* So the watchpoint data structure, will be like:
*
* NR_UCSAN_WP (generally is 4096 bytes size)
* slot 1 [0, 1, 2]
* NR_UCSAN_SLOT slot 2 [3, 4, 5]
* and so on...
*
* NOTE: The UCSAN_NR_WATCHPOINT is from config, we won't use it directly.
*/
#define NR_UCSAN_SLOT UCSAN_NR_WATCHPOINT
#define NR_UCSAN_WP (PAGE_SIZE / sizeof(atomic_long))
#define WP_SLOT(addr) ((addr >> PAGE_SHIFT) % NR_UCSAN_SLOT)
atomic_long watchpoints[NR_UCSAN_SLOT * NR_UCSAN_WP];

static __always_inline atomic_long *
find_watchpoint(unsigned long addr, size_t size, bool expect_write)
{
const unsigned long slot = WP_SLOT(addr);
unsigned long addr_masked = 0;
atomic_long *watchpoint = NULL;
int i;

/*
* First, we create the wp type of value from the paramters.
* We need to set up the write, size flags, and the addr mask.
*/
switch (size) {
case 1:
addr_masked = WATCHPOINT_SIZE_1;
break;
case 2:
addr_masked = WATCHPOINT_SIZE_2;
break;
case 4:
addr_masked = WATCHPOINT_SIZE_4;
break;
case 8:
addr_masked = WATCHPOINT_SIZE_8;
break;
case 16:
addr_masked = WATCHPOINT_SIZE_16;
break;
default:
/* TODO: format the error report */
printf("ERROR: Doesn't support the size\n");
exit(0);
}
if (expect_write)
addr_masked |= WATCHPOINT_WRITE_MASK;
addr_masked |= addr & WATCHPOINT_ADDR_MASK;

/*
* We set up the addr_masked, now we can travel the slots to find the
* corresponding watchpoint.
*/
for (i = 0; i < NR_UCSAN_WP; i++) {
/* We get the address of index first */
watchpoint = &watchpoints[slot + i];
/* if the watchpoint is what we want, cosume it. */
if (atomic_compare_exchange_strong(
watchpoint, &addr_masked,
addr_masked | WATCHPOINT_CONSUMED_MASK))
return watchpoint;
}

/* The corresponding watchpoint doesn't created. */
return NULL;
}

static __always_inline void check_access(const volatile void *ptr, size_t size,
int type, unsigned long ip)
{
atomic_long *watchpoint;

/*
* If the watchpoint found, it will consume the wacthpoint (set the
* WATCHPOINT_CONSUMED_MASK flag).
* Otherwise it will return NULL.
*/
watchpoint = find_watchpoint(
(unsigned long)ptr, size,
(type == UCSAN_ACCESS_WRITE | UCSAN_ACCESS_COMPOUND) ? true :
false);

/*
* TODO: determine the watchpoint created.
* ANd handle the data race detect.
*/
}

/*
Expand Down

0 comments on commit b3bb2d3

Please sign in to comment.