aboutsummaryrefslogtreecommitdiff
path: root/kernel/api/access.c
blob: 7dff240297808ede731100131c28ef20d7488656 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include "errno.h"
#include "globals.h"
#include <mm/mm.h>
#include <util/string.h>

#include "util/debug.h"

#include "mm/kmalloc.h"
#include "mm/mman.h"

#include "api/access.h"
#include "api/syscall.h"

static inline long userland_address(const void *addr)
{
    return addr >= (void *)USER_MEM_LOW && addr < (void *)USER_MEM_HIGH;
}

/*
 * Check for permissions on [uaddr, uaddr + nbytes), then
 * copy nbytes from userland address uaddr to kernel address kaddr.
 * Do not access the userland virtual addresses directly; instead,
 * use vmmap_read.
 */
long copy_from_user(void *kaddr, const void *uaddr, size_t nbytes)
{
    if (!range_perm(curproc, uaddr, nbytes, PROT_READ))
    {
        return -EFAULT;
    }
    KASSERT(userland_address(uaddr) && !userland_address(kaddr));
    return vmmap_read(curproc->p_vmmap, uaddr, kaddr, nbytes);
}

/*
 * Check for permissions on [uaddr, uaddr + nbytes), then
 * copy nbytes from kernel address kaddr to userland address uaddr.
 * Do not access the userland virtual addresses directly; instead,
 * use vmmap_write.
 */
long copy_to_user(void *uaddr, const void *kaddr, size_t nbytes)
{
    if (!range_perm(curproc, uaddr, nbytes, PROT_WRITE))
    {
        return -EFAULT;
    }
    KASSERT(userland_address(uaddr) && !userland_address(kaddr));
    return vmmap_write(curproc->p_vmmap, uaddr, kaddr, nbytes);
}

/*
 * Duplicate the string identified by ustr into kernel memory.
 * The kernel memory string kstr should be allocated using kmalloc.
 */
long user_strdup(argstr_t *ustr, char **kstrp)
{
    KASSERT(!userland_address(ustr));
    KASSERT(userland_address(ustr->as_str));

    *kstrp = kmalloc(ustr->as_len + 1);
    if (!*kstrp)
        return -ENOMEM;
    long ret = copy_from_user(*kstrp, ustr->as_str, ustr->as_len + 1);
    if (ret)
    {
        kfree(*kstrp);
        return ret;
    }
    return 0;
}

/*
 * Duplicate the string of vectors identified by uvec into kernel memory.
 * The vector itself (char**) and each string (char*) should be allocated
 * using kmalloc.
 */
long user_vecdup(argvec_t *uvec, char ***kvecp)
{
    KASSERT(!userland_address(uvec));
    KASSERT(userland_address(uvec->av_vec));

    char **kvec = kmalloc((uvec->av_len + 1) * sizeof(char *));
    *kvecp = kvec;

    if (!kvec)
    {
        return -ENOMEM;
    }
    memset(kvec, 0, (uvec->av_len + 1) * sizeof(char *));

    long ret = 0;
    for (size_t i = 0; i < uvec->av_len && !ret; i++)
    {
        argstr_t argstr;
        copy_from_user(&argstr, uvec->av_vec + i, sizeof(argstr_t));
        ret = user_strdup(&argstr, kvec + i);
    }

    if (ret)
    {
        for (size_t i = 0; i < uvec->av_len; i++)
            if (kvec[i])
                kfree(kvec[i]);
        kfree(kvec);
        *kvecp = NULL;
    }

    return ret;
}

/*
 * Return 1 if process p has permissions perm for virtual address vaddr;
 * otherwise return 0.
 * 
 * Check against the vmarea's protections on the mapping. 
 */
long addr_perm(proc_t *p, const void *vaddr, int perm)
{
    vmarea_t *vma = vmmap_lookup(p->p_vmmap, ADDR_TO_PN(vaddr));
    if (vma == NULL)
    {
        return 0;
    }

    return (vma->vma_prot & perm) == perm;
}


/*
 * Return 1 if process p has permissions perm for virtual address range [vaddr,
 * vaddr + len); otherwise return 0.
 * 
 * Hints: 
 * You can use addr_perm in your implementation.
 * Make sure to consider the case when the range of addresses that is being 
 * checked is less than a page. 
 */
long range_perm(proc_t *p, const void *vaddr, size_t len, int perm)
{
    size_t start = ADDR_TO_PN(vaddr);
    size_t end = ADDR_TO_PN(PAGE_ALIGN_UP(vaddr + len));
    while (start < end)
    {
        if (addr_perm(p, PN_TO_ADDR(start), perm) == 0)
        {
            return 0;
        }
        start++;
    }
    return 1;
}