Commit e148a8f9 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'uaccess.readdir' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull uaccess/readdir updates from Al Viro:
 "Finishing the conversion of readdir.c to unsafe_... API.

  This includes the uaccess_{read,write}_begin series by Christophe
  Leroy"

* 'uaccess.readdir' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  readdir.c: get rid of the last __put_user(), drop now-useless access_ok()
  readdir.c: get compat_filldir() more or less in sync with filldir()
  switch readdir(2) to unsafe_copy_dirent_name()
  drm/i915/gem: Replace user_access_begin by user_write_access_begin
  uaccess: Selectively open read or write user access
  uaccess: Add user_read_access_begin/end and user_write_access_begin/end
parents e0cd9206 5fb15141
...@@ -2794,7 +2794,8 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data, ...@@ -2794,7 +2794,8 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
* And this range already got effectively checked earlier * And this range already got effectively checked earlier
* when we did the "copy_from_user()" above. * when we did the "copy_from_user()" above.
*/ */
if (!user_access_begin(user_exec_list, count * sizeof(*user_exec_list))) if (!user_write_access_begin(user_exec_list,
count * sizeof(*user_exec_list)))
goto end; goto end;
for (i = 0; i < args->buffer_count; i++) { for (i = 0; i < args->buffer_count; i++) {
...@@ -2808,7 +2809,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data, ...@@ -2808,7 +2809,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
end_user); end_user);
} }
end_user: end_user:
user_access_end(); user_write_access_end();
end:; end:;
} }
......
...@@ -157,17 +157,18 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen, ...@@ -157,17 +157,18 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
} }
buf->result++; buf->result++;
dirent = buf->dirent; dirent = buf->dirent;
if (!access_ok(dirent, if (!user_write_access_begin(dirent,
(unsigned long)(dirent->d_name + namlen + 1) - (unsigned long)(dirent->d_name + namlen + 1) -
(unsigned long)dirent)) (unsigned long)dirent))
goto efault; goto efault;
if ( __put_user(d_ino, &dirent->d_ino) || unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
__put_user(offset, &dirent->d_offset) || unsafe_put_user(offset, &dirent->d_offset, efault_end);
__put_user(namlen, &dirent->d_namlen) || unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
__copy_to_user(dirent->d_name, name, namlen) || unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
__put_user(0, dirent->d_name + namlen)) user_write_access_end();
goto efault;
return 0; return 0;
efault_end:
user_write_access_end();
efault: efault:
buf->result = -EFAULT; buf->result = -EFAULT;
return -EFAULT; return -EFAULT;
...@@ -242,7 +243,7 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, ...@@ -242,7 +243,7 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
return -EINTR; return -EINTR;
dirent = buf->current_dir; dirent = buf->current_dir;
prev = (void __user *) dirent - prev_reclen; prev = (void __user *) dirent - prev_reclen;
if (!user_access_begin(prev, reclen + prev_reclen)) if (!user_write_access_begin(prev, reclen + prev_reclen))
goto efault; goto efault;
/* This might be 'dirent->d_off', but if so it will get overwritten */ /* This might be 'dirent->d_off', but if so it will get overwritten */
...@@ -251,14 +252,14 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, ...@@ -251,14 +252,14 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
unsafe_put_user(reclen, &dirent->d_reclen, efault_end); unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end); unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
user_access_end(); user_write_access_end();
buf->current_dir = (void __user *)dirent + reclen; buf->current_dir = (void __user *)dirent + reclen;
buf->prev_reclen = reclen; buf->prev_reclen = reclen;
buf->count -= reclen; buf->count -= reclen;
return 0; return 0;
efault_end: efault_end:
user_access_end(); user_write_access_end();
efault: efault:
buf->error = -EFAULT; buf->error = -EFAULT;
return -EFAULT; return -EFAULT;
...@@ -275,9 +276,6 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd, ...@@ -275,9 +276,6 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
}; };
int error; int error;
if (!access_ok(dirent, count))
return -EFAULT;
f = fdget_pos(fd); f = fdget_pos(fd);
if (!f.file) if (!f.file)
return -EBADF; return -EBADF;
...@@ -327,7 +325,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen, ...@@ -327,7 +325,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
return -EINTR; return -EINTR;
dirent = buf->current_dir; dirent = buf->current_dir;
prev = (void __user *)dirent - prev_reclen; prev = (void __user *)dirent - prev_reclen;
if (!user_access_begin(prev, reclen + prev_reclen)) if (!user_write_access_begin(prev, reclen + prev_reclen))
goto efault; goto efault;
/* This might be 'dirent->d_off', but if so it will get overwritten */ /* This might be 'dirent->d_off', but if so it will get overwritten */
...@@ -336,7 +334,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen, ...@@ -336,7 +334,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
unsafe_put_user(reclen, &dirent->d_reclen, efault_end); unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
unsafe_put_user(d_type, &dirent->d_type, efault_end); unsafe_put_user(d_type, &dirent->d_type, efault_end);
unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
user_access_end(); user_write_access_end();
buf->prev_reclen = reclen; buf->prev_reclen = reclen;
buf->current_dir = (void __user *)dirent + reclen; buf->current_dir = (void __user *)dirent + reclen;
...@@ -344,7 +342,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen, ...@@ -344,7 +342,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
return 0; return 0;
efault_end: efault_end:
user_access_end(); user_write_access_end();
efault: efault:
buf->error = -EFAULT; buf->error = -EFAULT;
return -EFAULT; return -EFAULT;
...@@ -361,9 +359,6 @@ int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, ...@@ -361,9 +359,6 @@ int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
}; };
int error; int error;
if (!access_ok(dirent, count))
return -EFAULT;
f = fdget_pos(fd); f = fdget_pos(fd);
if (!f.file) if (!f.file)
return -EBADF; return -EBADF;
...@@ -376,7 +371,7 @@ int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, ...@@ -376,7 +371,7 @@ int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
typeof(lastdirent->d_off) d_off = buf.ctx.pos; typeof(lastdirent->d_off) d_off = buf.ctx.pos;
lastdirent = (void __user *) buf.current_dir - buf.prev_reclen; lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
if (__put_user(d_off, &lastdirent->d_off)) if (put_user(d_off, &lastdirent->d_off))
error = -EFAULT; error = -EFAULT;
else else
error = count - buf.count; error = count - buf.count;
...@@ -424,17 +419,18 @@ static int compat_fillonedir(struct dir_context *ctx, const char *name, ...@@ -424,17 +419,18 @@ static int compat_fillonedir(struct dir_context *ctx, const char *name,
} }
buf->result++; buf->result++;
dirent = buf->dirent; dirent = buf->dirent;
if (!access_ok(dirent, if (!user_write_access_begin(dirent,
(unsigned long)(dirent->d_name + namlen + 1) - (unsigned long)(dirent->d_name + namlen + 1) -
(unsigned long)dirent)) (unsigned long)dirent))
goto efault; goto efault;
if ( __put_user(d_ino, &dirent->d_ino) || unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
__put_user(offset, &dirent->d_offset) || unsafe_put_user(offset, &dirent->d_offset, efault_end);
__put_user(namlen, &dirent->d_namlen) || unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
__copy_to_user(dirent->d_name, name, namlen) || unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
__put_user(0, dirent->d_name + namlen)) user_write_access_end();
goto efault;
return 0; return 0;
efault_end:
user_write_access_end();
efault: efault:
buf->result = -EFAULT; buf->result = -EFAULT;
return -EFAULT; return -EFAULT;
...@@ -471,7 +467,7 @@ struct compat_linux_dirent { ...@@ -471,7 +467,7 @@ struct compat_linux_dirent {
struct compat_getdents_callback { struct compat_getdents_callback {
struct dir_context ctx; struct dir_context ctx;
struct compat_linux_dirent __user *current_dir; struct compat_linux_dirent __user *current_dir;
struct compat_linux_dirent __user *previous; int prev_reclen;
int count; int count;
int error; int error;
}; };
...@@ -479,13 +475,17 @@ struct compat_getdents_callback { ...@@ -479,13 +475,17 @@ struct compat_getdents_callback {
static int compat_filldir(struct dir_context *ctx, const char *name, int namlen, static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type) loff_t offset, u64 ino, unsigned int d_type)
{ {
struct compat_linux_dirent __user * dirent; struct compat_linux_dirent __user *dirent, *prev;
struct compat_getdents_callback *buf = struct compat_getdents_callback *buf =
container_of(ctx, struct compat_getdents_callback, ctx); container_of(ctx, struct compat_getdents_callback, ctx);
compat_ulong_t d_ino; compat_ulong_t d_ino;
int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
namlen + 2, sizeof(compat_long_t)); namlen + 2, sizeof(compat_long_t));
int prev_reclen;
buf->error = verify_dirent_name(name, namlen);
if (unlikely(buf->error))
return buf->error;
buf->error = -EINVAL; /* only used if we fail.. */ buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count) if (reclen > buf->count)
return -EINVAL; return -EINVAL;
...@@ -494,29 +494,27 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen, ...@@ -494,29 +494,27 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
buf->error = -EOVERFLOW; buf->error = -EOVERFLOW;
return -EOVERFLOW; return -EOVERFLOW;
} }
dirent = buf->previous; prev_reclen = buf->prev_reclen;
if (dirent) { if (prev_reclen && signal_pending(current))
if (signal_pending(current)) return -EINTR;
return -EINTR;
if (__put_user(offset, &dirent->d_off))
goto efault;
}
dirent = buf->current_dir; dirent = buf->current_dir;
if (__put_user(d_ino, &dirent->d_ino)) prev = (void __user *) dirent - prev_reclen;
goto efault; if (!user_write_access_begin(prev, reclen + prev_reclen))
if (__put_user(reclen, &dirent->d_reclen))
goto efault;
if (copy_to_user(dirent->d_name, name, namlen))
goto efault;
if (__put_user(0, dirent->d_name + namlen))
goto efault;
if (__put_user(d_type, (char __user *) dirent + reclen - 1))
goto efault; goto efault;
buf->previous = dirent;
dirent = (void __user *)dirent + reclen; unsafe_put_user(offset, &prev->d_off, efault_end);
buf->current_dir = dirent; unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
user_write_access_end();
buf->prev_reclen = reclen;
buf->current_dir = (void __user *)dirent + reclen;
buf->count -= reclen; buf->count -= reclen;
return 0; return 0;
efault_end:
user_write_access_end();
efault: efault:
buf->error = -EFAULT; buf->error = -EFAULT;
return -EFAULT; return -EFAULT;
...@@ -526,7 +524,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd, ...@@ -526,7 +524,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
struct compat_linux_dirent __user *, dirent, unsigned int, count) struct compat_linux_dirent __user *, dirent, unsigned int, count)
{ {
struct fd f; struct fd f;
struct compat_linux_dirent __user * lastdirent;
struct compat_getdents_callback buf = { struct compat_getdents_callback buf = {
.ctx.actor = compat_filldir, .ctx.actor = compat_filldir,
.current_dir = dirent, .current_dir = dirent,
...@@ -534,9 +531,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd, ...@@ -534,9 +531,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
}; };
int error; int error;
if (!access_ok(dirent, count))
return -EFAULT;
f = fdget_pos(fd); f = fdget_pos(fd);
if (!f.file) if (!f.file)
return -EBADF; return -EBADF;
...@@ -544,8 +538,10 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd, ...@@ -544,8 +538,10 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
error = iterate_dir(f.file, &buf.ctx); error = iterate_dir(f.file, &buf.ctx);
if (error >= 0) if (error >= 0)
error = buf.error; error = buf.error;
lastdirent = buf.previous; if (buf.prev_reclen) {
if (lastdirent) { struct compat_linux_dirent __user * lastdirent;
lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
if (put_user(buf.ctx.pos, &lastdirent->d_off)) if (put_user(buf.ctx.pos, &lastdirent->d_off))
error = -EFAULT; error = -EFAULT;
else else
......
...@@ -378,6 +378,14 @@ extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count); ...@@ -378,6 +378,14 @@ extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count);
static inline unsigned long user_access_save(void) { return 0UL; } static inline unsigned long user_access_save(void) { return 0UL; }
static inline void user_access_restore(unsigned long flags) { } static inline void user_access_restore(unsigned long flags) { }
#endif #endif
#ifndef user_write_access_begin
#define user_write_access_begin user_access_begin
#define user_write_access_end user_access_end
#endif
#ifndef user_read_access_begin
#define user_read_access_begin user_access_begin
#define user_read_access_end user_access_end
#endif
#ifdef CONFIG_HARDENED_USERCOPY #ifdef CONFIG_HARDENED_USERCOPY
void usercopy_warn(const char *name, const char *detail, bool to_user, void usercopy_warn(const char *name, const char *detail, bool to_user,
......
...@@ -199,7 +199,7 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask, ...@@ -199,7 +199,7 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG); bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size); nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
if (!user_access_begin(umask, bitmap_size / 8)) if (!user_read_access_begin(umask, bitmap_size / 8))
return -EFAULT; return -EFAULT;
while (nr_compat_longs > 1) { while (nr_compat_longs > 1) {
...@@ -211,11 +211,11 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask, ...@@ -211,11 +211,11 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
} }
if (nr_compat_longs) if (nr_compat_longs)
unsafe_get_user(*mask, umask++, Efault); unsafe_get_user(*mask, umask++, Efault);
user_access_end(); user_read_access_end();
return 0; return 0;
Efault: Efault:
user_access_end(); user_read_access_end();
return -EFAULT; return -EFAULT;
} }
...@@ -228,7 +228,7 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask, ...@@ -228,7 +228,7 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG); bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size); nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
if (!user_access_begin(umask, bitmap_size / 8)) if (!user_write_access_begin(umask, bitmap_size / 8))
return -EFAULT; return -EFAULT;
while (nr_compat_longs > 1) { while (nr_compat_longs > 1) {
...@@ -239,10 +239,10 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask, ...@@ -239,10 +239,10 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
} }
if (nr_compat_longs) if (nr_compat_longs)
unsafe_put_user((compat_ulong_t)*mask, umask++, Efault); unsafe_put_user((compat_ulong_t)*mask, umask++, Efault);
user_access_end(); user_write_access_end();
return 0; return 0;
Efault: Efault:
user_access_end(); user_write_access_end();
return -EFAULT; return -EFAULT;
} }
......
...@@ -1558,7 +1558,7 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, ...@@ -1558,7 +1558,7 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
if (!infop) if (!infop)
return err; return err;
if (!user_access_begin(infop, sizeof(*infop))) if (!user_write_access_begin(infop, sizeof(*infop)))
return -EFAULT; return -EFAULT;
unsafe_put_user(signo, &infop->si_signo, Efault); unsafe_put_user(signo, &infop->si_signo, Efault);
...@@ -1567,10 +1567,10 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, ...@@ -1567,10 +1567,10 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
unsafe_put_user(info.pid, &infop->si_pid, Efault); unsafe_put_user(info.pid, &infop->si_pid, Efault);
unsafe_put_user(info.uid, &infop->si_uid, Efault); unsafe_put_user(info.uid, &infop->si_uid, Efault);
unsafe_put_user(info.status, &infop->si_status, Efault); unsafe_put_user(info.status, &infop->si_status, Efault);
user_access_end(); user_write_access_end();
return err; return err;
Efault: Efault:
user_access_end(); user_write_access_end();
return -EFAULT; return -EFAULT;
} }
...@@ -1685,7 +1685,7 @@ COMPAT_SYSCALL_DEFINE5(waitid, ...@@ -1685,7 +1685,7 @@ COMPAT_SYSCALL_DEFINE5(waitid,
if (!infop) if (!infop)
return err; return err;
if (!user_access_begin(infop, sizeof(*infop))) if (!user_write_access_begin(infop, sizeof(*infop)))
return -EFAULT; return -EFAULT;
unsafe_put_user(signo, &infop->si_signo, Efault); unsafe_put_user(signo, &infop->si_signo, Efault);
...@@ -1694,10 +1694,10 @@ COMPAT_SYSCALL_DEFINE5(waitid, ...@@ -1694,10 +1694,10 @@ COMPAT_SYSCALL_DEFINE5(waitid,
unsafe_put_user(info.pid, &infop->si_pid, Efault); unsafe_put_user(info.pid, &infop->si_pid, Efault);
unsafe_put_user(info.uid, &infop->si_uid, Efault); unsafe_put_user(info.uid, &infop->si_uid, Efault);
unsafe_put_user(info.status, &infop->si_status, Efault); unsafe_put_user(info.status, &infop->si_status, Efault);
user_access_end(); user_write_access_end();
return err; return err;
Efault: Efault:
user_access_end(); user_write_access_end();
return -EFAULT; return -EFAULT;
} }
#endif #endif
......
...@@ -116,9 +116,9 @@ long strncpy_from_user(char *dst, const char __user *src, long count) ...@@ -116,9 +116,9 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
kasan_check_write(dst, count); kasan_check_write(dst, count);
check_object_size(dst, count, false); check_object_size(dst, count, false);
if (user_access_begin(src, max)) { if (user_read_access_begin(src, max)) {
retval = do_strncpy_from_user(dst, src, count, max); retval = do_strncpy_from_user(dst, src, count, max);
user_access_end(); user_read_access_end();
return retval; return retval;
} }
} }
......
...@@ -109,9 +109,9 @@ long strnlen_user(const char __user *str, long count) ...@@ -109,9 +109,9 @@ long strnlen_user(const char __user *str, long count)
if (max > count) if (max > count)
max = count; max = count;
if (user_access_begin(str, max)) { if (user_read_access_begin(str, max)) {
retval = do_strnlen_user(str, count, max); retval = do_strnlen_user(str, count, max);
user_access_end(); user_read_access_end();
return retval; return retval;
} }
} }
......
...@@ -58,7 +58,7 @@ int check_zeroed_user(const void __user *from, size_t size) ...@@ -58,7 +58,7 @@ int check_zeroed_user(const void __user *from, size_t size)
from -= align; from -= align;
size += align; size += align;
if (!user_access_begin(from, size)) if (!user_read_access_begin(from, size))
return -EFAULT; return -EFAULT;
unsafe_get_user(val, (unsigned long __user *) from, err_fault); unsafe_get_user(val, (unsigned long __user *) from, err_fault);
...@@ -79,10 +79,10 @@ int check_zeroed_user(const void __user *from, size_t size) ...@@ -79,10 +79,10 @@ int check_zeroed_user(const void __user *from, size_t size)
val &= aligned_byte_mask(size); val &= aligned_byte_mask(size);
done: done:
user_access_end(); user_read_access_end();
return (val == 0); return (val == 0);
err_fault: err_fault:
user_access_end(); user_read_access_end();
return -EFAULT; return -EFAULT;
} }
EXPORT_SYMBOL(check_zeroed_user); EXPORT_SYMBOL(check_zeroed_user);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment