Skip to content

Commit

Permalink
kcov: add trace and trace_size to struct kcov_state
Browse files Browse the repository at this point in the history
Keep kcov_state.area as the pointer to the memory buffer used by
kcov and shared with the userspace. Store the pointer to the trace
(part of the buffer holding sequential events) separately, as we will
be splitting that buffer in multiple parts.
No functional change so far.

Signed-off-by: Alexander Potapenko <[email protected]>
  • Loading branch information
ramosian-glider committed Mar 4, 2025
1 parent 93702c5 commit 40153c0
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 24 deletions.
9 changes: 8 additions & 1 deletion include/linux/kcov-state.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@ struct kcov_state {
struct {
/* Size of the area (in long's). */
unsigned int size;
/*
* Pointer to user-provided memory used by kcov. This memory may
* contain multiple buffers.
*/
void *area;

/* Size of the trace (in long's). */
unsigned int trace_size;
/* Buffer for coverage collection, shared with the userspace. */
void *area;
unsigned long *trace;

/*
* KCOV sequence number: incremented each time kcov is
Expand Down
54 changes: 31 additions & 23 deletions kernel/kcov.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,11 @@ static notrace unsigned long canonicalize_ip(unsigned long ip)
return ip;
}

static void sanitizer_cov_write_subsequent(unsigned long *area, int size,
static void sanitizer_cov_write_subsequent(unsigned long *trace, int size,
unsigned long ip)
{
/* The first 64-bit word is the number of subsequent PCs. */
unsigned long pos = READ_ONCE(area[0]) + 1;
unsigned long pos = READ_ONCE(trace[0]) + 1;

if (likely(pos < size)) {
/*
Expand All @@ -207,9 +207,9 @@ static void sanitizer_cov_write_subsequent(unsigned long *area, int size,
* overitten by the recursive __sanitizer_cov_trace_pc().
* Update pos before writing pc to avoid such interleaving.
*/
WRITE_ONCE(area[0], pos);
WRITE_ONCE(trace[0], pos);
barrier();
area[pos] = ip;
trace[pos] = ip;
}
}

Expand All @@ -223,8 +223,8 @@ void notrace __sanitizer_cov_trace_pc(void)
if (!check_kcov_mode(KCOV_MODE_TRACE_PC, current))
return;

sanitizer_cov_write_subsequent(current->kcov_state.s.area,
current->kcov_state.s.size,
sanitizer_cov_write_subsequent(current->kcov_state.s.trace,
current->kcov_state.s.trace_size,
canonicalize_ip(_RET_IP_));
}
EXPORT_SYMBOL(__sanitizer_cov_trace_pc);
Expand All @@ -234,8 +234,8 @@ void notrace __sanitizer_cov_trace_pc_guard(u32 *guard)
if (!check_kcov_mode(KCOV_MODE_TRACE_PC, current))
return;

sanitizer_cov_write_subsequent(current->kcov_state.s.area,
current->kcov_state.s.size,
sanitizer_cov_write_subsequent(current->kcov_state.s.trace,
current->kcov_state.s.trace_size,
canonicalize_ip(_RET_IP_));
}
EXPORT_SYMBOL(__sanitizer_cov_trace_pc_guard);
Expand All @@ -250,9 +250,9 @@ EXPORT_SYMBOL(__sanitizer_cov_trace_pc_guard_init);
#ifdef CONFIG_KCOV_ENABLE_COMPARISONS
static void notrace write_comp_data(u64 type, u64 arg1, u64 arg2, u64 ip)
{
struct task_struct *t;
u64 *area;
u64 count, start_index, end_pos, max_pos;
struct task_struct *t;
u64 *trace;

t = current;
if (!check_kcov_mode(KCOV_MODE_TRACE_CMP, t))
Expand All @@ -264,22 +264,22 @@ static void notrace write_comp_data(u64 type, u64 arg1, u64 arg2, u64 ip)
* We write all comparison arguments and types as u64.
* The buffer was allocated for t->kcov_state.size unsigned longs.
*/
area = (u64 *)t->kcov_state.s.area;
trace = (u64 *)t->kcov_state.s.trace;
max_pos = t->kcov_state.s.size * sizeof(unsigned long);

count = READ_ONCE(area[0]);
count = READ_ONCE(trace[0]);

/* Every record is KCOV_WORDS_PER_CMP 64-bit words. */
start_index = 1 + count * KCOV_WORDS_PER_CMP;
end_pos = (start_index + KCOV_WORDS_PER_CMP) * sizeof(u64);
if (likely(end_pos <= max_pos)) {
/* See comment in sanitizer_cov_write_subsequent(). */
WRITE_ONCE(area[0], count + 1);
WRITE_ONCE(trace[0], count + 1);
barrier();
area[start_index] = type;
area[start_index + 1] = arg1;
area[start_index + 2] = arg2;
area[start_index + 3] = ip;
trace[start_index] = type;
trace[start_index + 1] = arg1;
trace[start_index + 2] = arg2;
trace[start_index + 3] = ip;
}
}

Expand Down Expand Up @@ -380,11 +380,13 @@ static void kcov_start(struct task_struct *t, struct kcov *kcov,

static void kcov_stop(struct task_struct *t)
{
int saved_sequence = t->kcov_state.s.sequence;

WRITE_ONCE(t->kcov_state.mode, KCOV_MODE_DISABLED);
barrier();
t->kcov = NULL;
t->kcov_state.s.size = 0;
t->kcov_state.s.area = NULL;
t->kcov_state.s = (typeof(t->kcov_state.s)){ 0 };
t->kcov_state.s.sequence = saved_sequence;
}

static void kcov_task_reset(struct task_struct *t)
Expand Down Expand Up @@ -733,6 +735,8 @@ static long kcov_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
}
kcov->state.s.area = area;
kcov->state.s.size = size;
kcov->state.s.trace = area;
kcov->state.s.trace_size = size;
kcov->state.mode = KCOV_MODE_INIT;
spin_unlock_irqrestore(&kcov->lock, flags);
return 0;
Expand Down Expand Up @@ -924,10 +928,12 @@ void kcov_remote_start(u64 handle)
local_lock_irqsave(&kcov_percpu_data.lock, flags);
}

/* Reset coverage size. */
*(u64 *)area = 0;
state.s.area = area;
state.s.size = size;
state.s.trace = area;
state.s.trace_size = size;
/* Reset coverage size. */
state.s.trace[0] = 0;

if (in_serving_softirq()) {
kcov_remote_softirq_start(t);
Expand Down Expand Up @@ -1000,8 +1006,8 @@ void kcov_remote_stop(void)
struct task_struct *t = current;
struct kcov *kcov;
unsigned int mode;
void *area;
unsigned int size;
void *area, *trace;
unsigned int size, trace_size;
int sequence;
unsigned long flags;

Expand Down Expand Up @@ -1033,6 +1039,8 @@ void kcov_remote_stop(void)
kcov = t->kcov;
area = t->kcov_state.s.area;
size = t->kcov_state.s.size;
trace = t->kcov_state.s.trace;
trace_size = t->kcov_state.s.trace_size;
sequence = t->kcov_state.s.sequence;

kcov_stop(t);
Expand Down

0 comments on commit 40153c0

Please sign in to comment.