aboutgitcodelistschat:MatrixIRC
path: root/cooker/emit.c
blob: 33355b662d0a647a589a6abbca4b4d8a55ba08d6 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                            


                                                                    
                                                                             


                                              
                                          



                   
                   
                 
                 
                            
 
                                 
                        
                                              
                 
                                       

                               
                                         

                            


            





                                                               
 



                                                              


                                               
                                              
 

                                                                           




                                             
   
























                                                                        






























                                                                              

                                                                
                                                                        





                                                                           
                                                                         





                                                                               
                                       
                                  
                   
 
                           
 




                                                                         












                                                                           
                                       
 
                                                                                



                                                                             


                                                                             






                                             





                                                                                


                                                              



                                                              




                                             
                         
 

                                                           




                                             

















                                                                     
   



































                                                                               

                                                                                     
                                                 



                                                   


                                                                





                                                              
                                






                                                              






                                                        











                                                                               
                                                                           

                                



                                                                         
  
                                                                     
   




                                                                       

                                                              
                                                        
                               
                                  
 
                              
 

                                                            

                                       






                                                           
 
                             
 
                                                                                
                           



                                                                               







                                             








                                                                     

                                                                          
 

                                                              

                               
 
                          
 










                                                        






                                                                               
 

                                             

 








                                                              

                                                               
                                                                   
                                       
 
                              
                                                                      


                      


                                                           


                                                   
   

                                                              


                                                              

                                 

                             
 

                                                           
 


                            
 
                      
 
                                                  




                                             









































                                                                               















                                                                   



                                                                        
 










                                                            
 
                       
                    
                 
                 

                                                  


                                                            

                                                 


                                                            
                 
 
                      
                  
                 








                                                                        
 
                      











                                                                         
                    
                                                  
                                                                        

                                             


                      
         
 
                   
 
 


                                                                    
                                                                         



















                                                                               




                                                                



                                                                  
                              







                                                       


                                                                              






                                                   



                                                                          





                                                                          

                                                                         
 
                             





                                                                               
 

                        









                                         
                               

 








                                                                             

                                     
                                   






                                                                               





                                                                                
 
// SPDX-License-Identifier: GPL-2.0-or-later

/* seitan - Syscall Expressive Interpreter, Transformer and Notifier
 *
 * cooker/emit.c - Generate gluten (bytecode) instructions and read-only data
 *
 * Copyright 2023 Red Hat GmbH
 * Author: Stefano Brivio <sbrivio@redhat.com>
 *         Alice Frosi <afrosi@redhat.com>
 */

#include "cooker.h"
#include "gluten.h"
#include "filter.h"
#include "util.h"
#include "emit.h"
#include "seccomp_profile.h"

static const char *type_str[] = {
	"UNDEF", "NONE",
	"USHORT", "INT", "U32", "U64", "LONG",
	"STRING",
	"STRUCT", "SELECT", "SELECTED",
	"PID",
	"PORT", "IPV4", "IPV6",
	"GNU_DEV_MAJOR", "GNU_DEV_MINOR",
	"FDPATH", "FDMOUNT",
	"UID", "GID",
	NULL
};

/**
 * emit_nr() - Emit OP_NR instruction: jump on syscall mismatch
 * @g:		gluten context
 * @number:	Pointer to system call number
 */
void emit_nr(struct gluten_ctx *g, struct gluten_offset number)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_nr *nr = &op->op.nr;

	op->type = OP_NR;

	nr->nr = number;
	nr->no_match.type = OFFSET_INSTRUCTION;
	nr->no_match.offset = JUMP_NEXT_BLOCK;

	debug("   %i: OP_NR: if syscall number is not C#%lu, jump to %s",
	      g->ip.offset, number.offset, jump_name[nr->no_match.offset]);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

/**
 * emit_fd() - Emit OP_FD instruction: add/set file descriptor in target
 * @g:		gluten context
 * @desc:	Pointer to file descriptors specification
 */
void emit_fd(struct gluten_ctx *g, struct fd_desc *desc)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_fd *fd = &op->op.fd;
	struct gluten_offset o;
	struct fd_desc *dst;

	op->type = OP_FD;

	o = gluten_ro_alloc(g, sizeof(struct fd_desc));
	dst = (struct fd_desc *)gluten_ptr(&g->g, o);
	memcpy(dst, desc, sizeof(struct fd_desc));
	fd->desc = o;

	debug("   %i: OP_FD: ...", g->ip.offset);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

/**
 * emit_fdget() - Emit OP_FDGET instruction: copy file descriptor from target
 * @g:		gluten context
 * @src:	Pointer to existing file descriptor number in target process
 * @dst:	Pointer to new descriptor number in seitan, can be OFFSET_NULL
 *
 * Return: offset to destination operand, allocated here if not given
 */
struct gluten_offset emit_fdget(struct gluten_ctx *g,
				struct gluten_offset src,
				struct gluten_offset dst)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_fdget *fdget = &op->op.fdget;

	op->type = OP_FDGET;

	if (dst.type == OFFSET_NULL)
		dst = gluten_rw_alloc(g, sizeof(int));

	fdget->src = src;
	fdget->dst = dst;

	debug("   %i: OP_FDGET: ...", g->ip.offset);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");

	return dst;
}

/**
 * emit_call() - Emit OP_CALL instruction: execute a system call
 * @g:		gluten context
 * @context:	CONTEXT_SPEC_NONE-terminated array of context references
 * @nr:		System call number
 * @count:	Argument count
 * @is_ptr:	Array indicating whether arguments need to be dereferenced
 * @args:	Offsets of arguments
 * @ret_offset:	Offset where return value must be saved, can be OFFSET_NULL
 */
void emit_call(struct gluten_ctx *g, struct context_desc *cdesc, long nr,
	       unsigned count, bool is_ptr[6],
	       struct gluten_offset offset[6], struct gluten_offset ret_offset)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct gluten_offset o1 = { 0 }, o2 = { 0 };
	struct op_call *call = &op->op.call;
	struct context_desc *c = cdesc;
	struct syscall_desc *desc;
	unsigned i;

	op->type = OP_CALL;

	for (i = 0; c[i].spec != CONTEXT_SPEC_NONE; i++);
	if (i) {
		o1 = gluten_ro_alloc(g, sizeof(struct context_desc) * i);
		c = (struct context_desc *)gluten_ptr(&g->g, o1);
		memcpy(c, cdesc, sizeof(struct context_desc) * i);
	}

	o2 = gluten_ro_alloc(g, sizeof(struct syscall_desc) +
			       sizeof(struct gluten_offset) *
			       (count + (ret_offset.type != OFFSET_NULL)));
	desc = (struct syscall_desc *)gluten_ptr(&g->g, o2);
	desc->nr = nr;
	desc->arg_count = count;
	desc->has_ret = ret_offset.type != OFFSET_NULL;
	for (i = 0; i < count; i++)
		desc->arg_deref |= BIT(i) * is_ptr[i];
	desc->context = o1;
	memcpy(desc->args, offset, sizeof(struct gluten_offset) * count);
	desc->args[count] = ret_offset;

	debug("   %i: OP_CALL: %s, arguments:", g->ip.offset, syscall_name(nr));
	for (i = 0; i < count; i++) {
		debug("\t%i: %s %s%i", i, gluten_offset_name[offset[i].type],
		      is_ptr[i] ? "*" : "", offset[i].offset);
	}
	if (desc->has_ret)
		debug("\treturn: %s %i", gluten_offset_name[ret_offset.type],
		      offset[i].offset);
	call->desc = o2;

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

/**
 * emit_load() - Emit OP_LOAD instruction: dereference and copy syscall argument
 * @g:		gluten context
 * @dst:	gluten destination to copy dereferenced data
 * @index:	Index of system call argument
 * @len:	Length of data item pointed by reference
 */
void emit_load(struct gluten_ctx *g, struct gluten_offset dst,
	       int index, size_t len)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_load *load = &op->op.load;

	op->type = OP_LOAD;

	load->src.type = OFFSET_SECCOMP_DATA;
	load->src.offset = index;

	load->dst = dst;
	load->size = len;

	debug("   %i: OP_LOAD: #%i < args[%i] (size: %lu)",
	      g->ip.offset, dst.offset, index, len);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

void emit_store(struct gluten_ctx *g, struct gluten_offset dst,
		struct gluten_offset src, struct gluten_offset count)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_store *store = &op->op.store;

	op->type = OP_STORE;

	store->dst = dst;
	store->src = src;
	store->count = count;

	debug("   %i: OP_STORE: #%i", g->ip.offset);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

/**
 * emit_iovload() - Emit OP_IOVLOAD instruction: IO vector from loaded pointers
 * @g:		gluten context
 * @iov:	Pointer to msg_iov, already stored in gluten
 * @iovlen:	Pointer to msg_iovlen, already stored in gluten
 * @dst:	gluten destination to copy dereferenced data
 * @len:	Maximum length of data to copy altogether
 */
struct gluten_offset emit_iovload(struct gluten_ctx *g,
				  struct gluten_offset iov,
				  struct gluten_offset iovlen,
				  size_t len)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_iovload *load = &op->op.iovload;
	struct gluten_offset dst;

	dst = gluten_rw_alloc(g, len);

	op->type = OP_IOVLOAD;

	load->iov = iov;
	load->iovlen = iovlen;

	load->dst = dst;
	load->size = len;

	debug("   %i: OP_IOVLOAD: #%i < (#%i) as iovec (size: %lu)",
	      g->ip.offset, dst.offset, iov.offset, len);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");

	return dst;
}

/**
 * emit_resolved() - Emit OP_RESOLVEFD instruction: resolve file descriptor with path
 * @g:		gluten context
 * @type:	Type of field (FDPATH or FDMOUNT)
 * @fd:		offset of the file descriptor value
 * @path:	offset of the path
 * @path_size:	size of the path
 */
void emit_resolvefd(struct gluten_ctx *g, enum type type,
		    struct gluten_offset fd,
		    struct gluten_offset path, size_t path_size)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_resolvefd *resfd = &op->op.resfd;
	struct gluten_offset o;
	struct resolvefd_desc *desc;

	op->type = OP_RESOLVEFD;
        o = gluten_ro_alloc(g, sizeof(struct resolvefd_desc));
        desc = (struct resolvefd_desc *)gluten_ptr(&g->g, o);

	desc->fd = fd;
	desc->path = path;
	desc->path_max = path_size;

	if (type == FDPATH)
		desc->type = RESOLVEFD_PATH;
	else if (type == FDMOUNT)
		desc->type = RESOLVEFD_MOUNT;
	else
		die("Invalid field type for resolvefd");

	resfd->desc = o;

	debug("   %i: OP_RESOLVEDFD:", g->ip.offset);
	debug("   \tfd: %s offset=%d", gluten_offset_name[fd.type], fd.offset);
	debug("   \tpath: %s offset=%d size=%d", gluten_offset_name[path.type],
	      path.offset, path_size);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

/**
 * emit_bitwise(): Emit OP_BITWISE instruction: bitwise operation and store
 * @g:		gluten context
 * @type:	Type of operands
 * @op_type:	Type of bitwise operation
 * @dst:	gluten pointer to destination operand, can be OFFSET_NULL
 * @x:		gluten pointer to first source operand
 * @y:		gluten pointer to second source operand
 *
 * Return: offset to destination operand, allocated here if not given
 */
struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type,
				  enum bitwise_type op_type,
				  struct gluten_offset dst,
				  struct gluten_offset x,
				  struct gluten_offset y)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_bitwise *op_bitwise = &op->op.bitwise;
	struct gluten_offset o;
	struct bitwise_desc *desc;

	op->type = OP_BITWISE;

	o = gluten_ro_alloc(g, sizeof(struct bitwise_desc));
	desc = (struct bitwise_desc *)gluten_ptr(&g->g, o);

	desc->size = gluten_size[type];
	desc->type = op_type;
	if (dst.type == OFFSET_NULL)
		desc->dst = gluten_rw_alloc(g, desc->size);
	else
		desc->dst = dst;
	desc->x    = x;
	desc->y    = y;

	op_bitwise->desc = o;

	debug("   %i: OP_BITWISE: %s: #%lu (size: %lu) := %s: #%lu %s %s: #%lu",
	      g->ip.offset,
	      gluten_offset_name[desc->dst.type], desc->dst.offset, desc->size,
	      gluten_offset_name[desc->x.type],   desc->x.offset,
	      bitwise_type_str[op_type],
	      gluten_offset_name[desc->y.type],   desc->y.offset);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");

	return desc->dst;
}

/**
 * emit_cmp(): Emit OP_CMP instruction: compare data from two offsets
 * @g:		gluten context
 * @cmp_type:	Type of comparison
 * @x:		gluten pointer to first operand of comparison
 * @y:		gluten pointer to second operand of comparison
 * @size:	Size of comparison
 * @jmp:	Jump direction if comparison is true
 */
void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type,
	      struct gluten_offset x, struct gluten_offset y, size_t size,
	      enum jump_type jmp)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_cmp *cmp = &op->op.cmp;
	struct gluten_offset o;
	struct cmp_desc *desc;

	op->type = OP_CMP;

	o = gluten_ro_alloc(g, sizeof(struct cmp_desc));
	desc = (struct cmp_desc *)gluten_ptr(&g->g, o);

	desc->x = x;
	desc->y = y;
	desc->size = size;
	desc->cmp = cmp_type;
	desc->jmp.type = OFFSET_INSTRUCTION;
	desc->jmp.offset = jmp;

	cmp->desc = o;

	debug("   %i: OP_CMP: if %s: #%lu %s (size: %lu) %s: #%lu, jump to %s",
	      g->ip.offset,
	      gluten_offset_name[x.type], x.offset,
	      cmp_type_str[cmp_type], size,
	      gluten_offset_name[y.type], y.offset,
	      jump_name[jmp]);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

/**
 * emit_cmp_field() - Emit OP_CMP for a given field type
 * @g:		gluten context
 * @cmp:	Type of comparison
 * @field:	Description of field from system call model
 * @x:		gluten pointer to first operand of comparison
 * @y:		gluten pointer to second operand of comparison
 * @jmp:	Jump direction if comparison is true
 */
void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp,
		    struct field *field,
		    struct gluten_offset x, struct gluten_offset y,
		    enum jump_type jmp)
{
	emit_cmp(g, cmp, x, y,
		 field->size ? field->size : gluten_size[field->type],
		 jmp);
}

/**
 * emit_return() - Emit OP_RETURN instruction: return value
 * @g:		gluten context
 * @v:		Offset of return value
 * @error:	Offset of error value
 * @cont:	Continue syscall execution, if true
 */
void emit_return(struct gluten_ctx *g, struct gluten_offset v,
		 struct gluten_offset error, bool cont)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_return *ret = &op->op.ret;
	struct gluten_offset o;
	struct return_desc *desc;

	op->type = OP_RETURN;

	o = gluten_ro_alloc(g, sizeof(struct return_desc));
	desc = (struct return_desc *)gluten_ptr(&g->g, o);

	desc->val = v;
	desc->error = error;
	desc->cont = cont;

	ret->desc = o;

	debug("   %i: OP_RETURN:",  g->ip.offset);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

/**
 * emit_copy(): Emit OP_COPY instruction: copy between given offsets
 * @g:		gluten context
 * @dst:	gluten pointer to destination
 * @src:	gluten pointer to source
 * @size:	Bytes to copy
 */
void emit_copy(struct gluten_ctx *g,
	       struct gluten_offset dst, struct gluten_offset src, size_t size)
{
	struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);
	struct op_copy *copy = &op->op.copy;

	op->type = OP_COPY;

	copy->dst = dst;
	copy->src = src;
	copy->size = size;

	debug("   %i: OP_COPY: %lu bytes from %s: #%lu to %s: #%lu",
	      g->ip.offset, size,
	      gluten_offset_name[src.type], src.offset,
	      gluten_offset_name[dst.type], dst.offset);

	if (++g->ip.offset > INST_MAX)
		die("Too many instructions");
}

/**
 * emit_copy_field() - Emit OP_COPY for a given field type
 * @g:		gluten context
 * @field:	Description of field from system call model
 * @dst:	gluten pointer to destination
 * @src:	gluten pointer to source
 */
void emit_copy_field(struct gluten_ctx *g, struct field *field,
		     struct gluten_offset dst, struct gluten_offset src)
{
	emit_copy(g, dst, src,
		  field->size ? field->size : gluten_size[field->type]);
}

/**
 * emit_end() - Emit OP_END instruction: end of the operation block
 * @g:         gluten context
 */
void emit_end(struct gluten_ctx *g)
{
       struct op *op = (struct op *)gluten_ptr(&g->g, g->ip);

       op->type = OP_END;

       debug("   %i: OP_END",  g->ip.offset);

       if (++g->ip.offset > INST_MAX)
               die("Too many instructions");
}

static struct gluten_offset emit_data_do(struct gluten_ctx *g,
					 struct gluten_offset offset,
					 enum type type, size_t str_len,
					 union value *value, bool add)
{
	void *p = gluten_ptr(&g->g, offset);
	struct gluten_offset ret = offset;

	if (!p) {
		if (type == STRING)
			ret = gluten_ro_alloc(g, str_len);
		else
			ret = gluten_ro_alloc_type(g, type);

		p = gluten_ptr(&g->g, ret);
	}

	switch (type) {
	case USHORT:
	case INT:
	case U32:
		if (add) {
			*(int *)p |= value->v_int;
			debug("   C#%i |= (%s) %i (0x%04x)",
			      ret.offset, type_str[type],
			      value->v_num, value->v_num);
		} else {
			*(int *)p = value->v_int;
			debug("   C#%i := (%s) %i (0x%04x)",
			      ret.offset, type_str[type],
			      value->v_num, value->v_num);
		}

		break;
	case LONG:
	case U64:
		if (add) {
			*(uint64_t *)p |= value->v_num;
			debug("   C#%i |= (%s) %llu",
			      ret.offset, type_str[type], value->v_num);
		} else {
			*(uint64_t *)p = value->v_num;
			debug("   C#%i := (%s) %llu",
			      ret.offset, type_str[type], value->v_num);
		}

		break;
	case GNU_DEV_MAJOR:
		*(unsigned long long int *)p |= makedev(value->v_num, 0);
		debug("   C#%i |= (%s) %llu",
		      ret.offset, type_str[type], value->v_num);

		break;
	case GNU_DEV_MINOR:
		*(unsigned long long int *)p |= makedev(0, value->v_num);
		debug("   C#%i |= (%s) %llu",
		      ret.offset, type_str[type], value->v_num);

		break;
	case STRING:
		strncpy(p, value->v_str, str_len);
		debug("   C#%i: (%s:%i) %s", ret.offset, type_str[type],
		      str_len, value->v_str);

		break;
	default:
		;
	}

	return ret;
}

struct gluten_offset emit_data(struct gluten_ctx *g, enum type type,
			       size_t str_len, union value *value)
{
	return emit_data_do(g, NULL_OFFSET, type, str_len, value, false);
}

struct gluten_offset emit_data_at(struct gluten_ctx *g,
				  struct gluten_offset offset,
				  enum type type, union value *value)
{
	return emit_data_do(g, offset, type,
			    (type == STRING) ? strlen(value->v_str) : 0, value,
			    false);
}

struct gluten_offset emit_data_or(struct gluten_ctx *g,
				  struct gluten_offset offset,
				  enum type type, union value *value)
{
	return emit_data_do(g, offset, type,
			    (type == STRING) ? strlen(value->v_str) : 0, value,
			    true);
}

struct gluten_offset emit_seccomp_data(int index) {
	struct gluten_offset o = { OFFSET_SECCOMP_DATA, index };
	return o;
}

static void gluten_link(struct gluten_ctx *g, enum jump_type type,
			struct op *start)
{
	struct gluten_offset *jmp;
	struct cmp_desc *desc;
	struct op *op;

	for (op = (struct op *)start; op->type; op++) {
		switch (op->type) {
		case OP_NR:
			jmp = &op->op.nr.no_match;
			break;
		case OP_CMP:
			desc = (struct cmp_desc *)gluten_ptr(&g->g,
							     op->op.cmp.desc);
			jmp = &desc->jmp;
			break;
		default:
			continue;
		}

		if (jmp->offset == type) {
			jmp->offset = g->ip.offset;

			if (jmp->offset >= INST_MAX)
				die("jump after end of instruction area");

			debug("    linked jump of instruction #%i to #%i",
			      op - (struct op *)g->g.inst, g->ip.offset);
		}
	}
}

static void emit_bpf_filter_arg(int index, enum type type, union value v,
				union value mask, enum op_cmp_type cmp)
{
	struct bpf_field bpf;

	/* gluten uses the comparison to skip to the next match, the BPF filter
	 * uses it to notify instead.
	 */
	if (mask.v_num)
		bpf.cmp = (cmp == CMP_EQ) ? AND_NE : AND_EQ;

	bpf.arg = index;

	if (TYPE_IS_64BIT(type)) {
		bpf.value.v64 = v.v_num;
		bpf.op2.v64 = mask.v_num;
		bpf.type = BPF_U64;
	} else {
		bpf.value.v32 = v.v_num;
		bpf.op2.v32 = mask.v_num;
		bpf.type = BPF_U32;
	}

	filter_add_check(&bpf);
}

void emit_bpf_arg(int index, enum type type, union value v, union value mask,
		  enum op_cmp_type cmp, enum scmp_mode mode)
{
	if (mode == SCMP_FILTER)
		emit_bpf_filter_arg(index, type, v, mask, cmp);
	else
		scmp_profile_add_check(index, v, mask, cmp);
}

void link_block(struct gluten_ctx *g)
{
	debug(" Linking block...");
	gluten_link(g, JUMP_NEXT_BLOCK, (struct op *)gluten_ptr(&g->g, g->lr));
}

void link_match(struct gluten_ctx *g)
{
	debug("   Linking match...");
	gluten_link(g, JUMP_NEXT_MATCH, (struct op *)gluten_ptr(&g->g, g->mr));
}

void link_matches(struct gluten_ctx *g)
{
	debug("   Linking matches...");
	gluten_link(g, JUMP_NEXT_ACTION, (struct op *)gluten_ptr(&g->g, g->lr));
}