License cleanup: add SPDX GPL-2.0 license identifier to files with no license
Many source files in the tree are missing licensing information, which
makes it harder for compliance tools to determine the correct license.
By default all files without license information are under the default
license of the kernel, which is GPL version 2.
Update the files which contain no license information with the 'GPL-2.0'
SPDX license identifier. The SPDX identifier is a legally binding
shorthand, which can be used instead of the full boiler plate text.
This patch is based on work done by Thomas Gleixner and Kate Stewart and
Philippe Ombredanne.
How this work was done:
Patches were generated and checked against linux-4.14-rc6 for a subset of
the use cases:
- file had no licensing information it it.
- file was a */uapi/* one with no licensing information in it,
- file was a */uapi/* one with existing licensing information,
Further patches will be generated in subsequent months to fix up cases
where non-standard license headers were used, and references to license
had to be inferred by heuristics based on keywords.
The analysis to determine which SPDX License Identifier to be applied to
a file was done in a spreadsheet of side by side results from of the
output of two independent scanners (ScanCode & Windriver) producing SPDX
tag:value files created by Philippe Ombredanne. Philippe prepared the
base worksheet, and did an initial spot review of a few 1000 files.
The 4.13 kernel was the starting point of the analysis with 60,537 files
assessed. Kate Stewart did a file by file comparison of the scanner
results in the spreadsheet to determine which SPDX license identifier(s)
to be applied to the file. She confirmed any determination that was not
immediately clear with lawyers working with the Linux Foundation.
Criteria used to select files for SPDX license identifier tagging was:
- Files considered eligible had to be source code files.
- Make and config files were included as candidates if they contained >5
lines of source
- File already had some variant of a license header in it (even if <5
lines).
All documentation files were explicitly excluded.
The following heuristics were used to determine which SPDX license
identifiers to apply.
- when both scanners couldn't find any license traces, file was
considered to have no license information in it, and the top level
COPYING file license applied.
For non */uapi/* files that summary was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 11139
and resulted in the first patch in this series.
If that file was a */uapi/* path one, it was "GPL-2.0 WITH
Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 WITH Linux-syscall-note 930
and resulted in the second patch in this series.
- if a file had some form of licensing information in it, and was one
of the */uapi/* ones, it was denoted with the Linux-syscall-note if
any GPL family license was found in the file or had no licensing in
it (per prior point). Results summary:
SPDX license identifier # files
---------------------------------------------------|------
GPL-2.0 WITH Linux-syscall-note 270
GPL-2.0+ WITH Linux-syscall-note 169
((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21
((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17
LGPL-2.1+ WITH Linux-syscall-note 15
GPL-1.0+ WITH Linux-syscall-note 14
((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5
LGPL-2.0+ WITH Linux-syscall-note 4
LGPL-2.1 WITH Linux-syscall-note 3
((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3
((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1
and that resulted in the third patch in this series.
- when the two scanners agreed on the detected license(s), that became
the concluded license(s).
- when there was disagreement between the two scanners (one detected a
license but the other didn't, or they both detected different
licenses) a manual inspection of the file occurred.
- In most cases a manual inspection of the information in the file
resulted in a clear resolution of the license that should apply (and
which scanner probably needed to revisit its heuristics).
- When it was not immediately clear, the license identifier was
confirmed with lawyers working with the Linux Foundation.
- If there was any question as to the appropriate license identifier,
the file was flagged for further research and to be revisited later
in time.
In total, over 70 hours of logged manual review was done on the
spreadsheet to determine the SPDX license identifiers to apply to the
source files by Kate, Philippe, Thomas and, in some cases, confirmation
by lawyers working with the Linux Foundation.
Kate also obtained a third independent scan of the 4.13 code base from
FOSSology, and compared selected files where the other two scanners
disagreed against that SPDX file, to see if there was new insights. The
Windriver scanner is based on an older version of FOSSology in part, so
they are related.
Thomas did random spot checks in about 500 files from the spreadsheets
for the uapi headers and agreed with SPDX license identifier in the
files he inspected. For the non-uapi files Thomas did random spot checks
in about 15000 files.
In initial set of patches against 4.14-rc6, 3 files were found to have
copy/paste license identifier errors, and have been fixed to reflect the
correct identifier.
Additionally Philippe spent 10 hours this week doing a detailed manual
inspection and review of the 12,461 patched files from the initial patch
version early this week with:
- a full scancode scan run, collecting the matched texts, detected
license ids and scores
- reviewing anything where there was a license detected (about 500+
files) to ensure that the applied SPDX license was correct
- reviewing anything where there was no detection but the patch license
was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied
SPDX license was correct
This produced a worksheet with 20 files needing minor correction. This
worksheet was then exported into 3 different .csv files for the
different types of files to be modified.
These .csv files were then reviewed by Greg. Thomas wrote a script to
parse the csv files and add the proper SPDX tag to the file, in the
format that the file expected. This script was further refined by Greg
based on the output to detect more types of files automatically and to
distinguish between header and source .c files (which need different
comment types.) Finally Greg ran the script using the .csv files to
generate the patches.
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-01 22:07:57 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/fs/seq_file.c
|
|
|
|
*
|
|
|
|
* helper functions for making synthetic files from sequences of records.
|
|
|
|
* initial implementation -- AV, Oct 2001.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
2011-11-17 12:57:37 +08:00
|
|
|
#include <linux/export.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/seq_file.h>
|
2014-07-03 06:22:37 +08:00
|
|
|
#include <linux/vmalloc.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/slab.h>
|
2012-05-24 08:01:20 +08:00
|
|
|
#include <linux/cred.h>
|
2014-07-03 06:22:37 +08:00
|
|
|
#include <linux/mm.h>
|
2015-09-10 06:38:33 +08:00
|
|
|
#include <linux/printk.h>
|
2015-11-07 08:32:40 +08:00
|
|
|
#include <linux/string_helpers.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-12-25 03:46:01 +08:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/page.h>
|
|
|
|
|
2012-03-24 06:02:55 +08:00
|
|
|
static void seq_set_overflow(struct seq_file *m)
|
|
|
|
{
|
|
|
|
m->count = m->size;
|
|
|
|
}
|
|
|
|
|
2014-07-03 06:22:37 +08:00
|
|
|
static void *seq_buf_alloc(unsigned long size)
|
|
|
|
{
|
2017-05-09 06:57:09 +08:00
|
|
|
return kvmalloc(size, GFP_KERNEL);
|
2014-07-03 06:22:37 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/**
|
|
|
|
* seq_open - initialize sequential file
|
|
|
|
* @file: file we initialize
|
|
|
|
* @op: method table describing the sequence
|
|
|
|
*
|
|
|
|
* seq_open() sets @file, associating it with a sequence described
|
|
|
|
* by @op. @op->start() sets the iterator up and returns the first
|
|
|
|
* element of sequence. @op->stop() shuts it down. @op->next()
|
|
|
|
* returns the next element of sequence. @op->show() prints element
|
|
|
|
* into the buffer. In case of error ->start() and ->next() return
|
|
|
|
* ERR_PTR(error). In the end of sequence they return %NULL. ->show()
|
|
|
|
* returns 0 in case of success and negative number in case of error.
|
2008-03-28 12:46:41 +08:00
|
|
|
* Returning SEQ_SKIP means "discard this element and move on".
|
2015-07-01 05:57:36 +08:00
|
|
|
* Note: seq_open() will allocate a struct seq_file and store its
|
|
|
|
* pointer in @file->private_data. This pointer should not be modified.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2006-12-07 12:40:36 +08:00
|
|
|
int seq_open(struct file *file, const struct seq_operations *op)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-07-01 05:57:33 +08:00
|
|
|
struct seq_file *p;
|
|
|
|
|
|
|
|
WARN_ON(file->private_data);
|
|
|
|
|
|
|
|
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
|
|
|
if (!p)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
file->private_data = p;
|
2005-11-08 06:15:34 +08:00
|
|
|
|
2006-03-23 19:00:37 +08:00
|
|
|
mutex_init(&p->lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
p->op = op;
|
Make file credentials available to the seqfile interfaces
A lot of seqfile users seem to be using things like %pK that uses the
credentials of the current process, but that is actually completely
wrong for filesystem interfaces.
The unix semantics for permission checking files is to check permissions
at _open_ time, not at read or write time, and that is not just a small
detail: passing off stdin/stdout/stderr to a suid application and making
the actual IO happen in privileged context is a classic exploit
technique.
So if we want to be able to look at permissions at read time, we need to
use the file open credentials, not the current ones. Normal file
accesses can just use "f_cred" (or any of the helper functions that do
that, like file_ns_capable()), but the seqfile interfaces do not have
any such options.
It turns out that seq_file _does_ save away the user_ns information of
the file, though. Since user_ns is just part of the full credential
information, replace that special case with saving off the cred pointer
instead, and suddenly seq_file has all the permission information it
needs.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-15 02:22:00 +08:00
|
|
|
|
|
|
|
// No refcounting: the lifetime of 'p' is constrained
|
|
|
|
// to the lifetime of the file.
|
|
|
|
p->file = file;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Wrappers around seq_open(e.g. swaps_open) need to be
|
|
|
|
* aware of this. If they set f_version themselves, they
|
|
|
|
* should call seq_open first and then set f_version.
|
|
|
|
*/
|
|
|
|
file->f_version = 0;
|
|
|
|
|
2009-02-19 06:48:16 +08:00
|
|
|
/*
|
|
|
|
* seq_files support lseek() and pread(). They do not implement
|
|
|
|
* write() at all, but we clear FMODE_PWRITE here for historical
|
|
|
|
* reasons.
|
|
|
|
*
|
|
|
|
* If a client of seq_files a) implements file.write() and b) wishes to
|
|
|
|
* support pwrite() then that client will need to implement its own
|
|
|
|
* file.open() which calls seq_open() and then sets FMODE_PWRITE.
|
|
|
|
*/
|
|
|
|
file->f_mode &= ~FMODE_PWRITE;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_open);
|
|
|
|
|
2009-02-05 07:12:25 +08:00
|
|
|
static int traverse(struct seq_file *m, loff_t offset)
|
|
|
|
{
|
|
|
|
loff_t pos = 0, index;
|
|
|
|
int error = 0;
|
|
|
|
void *p;
|
|
|
|
|
|
|
|
m->version = 0;
|
|
|
|
index = 0;
|
|
|
|
m->count = m->from = 0;
|
|
|
|
if (!offset) {
|
|
|
|
m->index = index;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!m->buf) {
|
2014-07-03 06:22:37 +08:00
|
|
|
m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
|
2009-02-05 07:12:25 +08:00
|
|
|
if (!m->buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
p = m->op->start(m, &index);
|
|
|
|
while (p) {
|
|
|
|
error = PTR_ERR(p);
|
|
|
|
if (IS_ERR(p))
|
|
|
|
break;
|
|
|
|
error = m->op->show(m, p);
|
|
|
|
if (error < 0)
|
|
|
|
break;
|
|
|
|
if (unlikely(error)) {
|
|
|
|
error = 0;
|
|
|
|
m->count = 0;
|
|
|
|
}
|
2014-09-30 07:08:21 +08:00
|
|
|
if (seq_has_overflowed(m))
|
2009-02-05 07:12:25 +08:00
|
|
|
goto Eoverflow;
|
|
|
|
if (pos + m->count > offset) {
|
|
|
|
m->from = offset - pos;
|
|
|
|
m->count -= m->from;
|
|
|
|
m->index = index;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pos += m->count;
|
|
|
|
m->count = 0;
|
|
|
|
if (pos == offset) {
|
|
|
|
index++;
|
|
|
|
m->index = index;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p = m->op->next(m, p, &index);
|
|
|
|
}
|
|
|
|
m->op->stop(m, p);
|
2009-02-06 05:30:05 +08:00
|
|
|
m->index = index;
|
2009-02-05 07:12:25 +08:00
|
|
|
return error;
|
|
|
|
|
|
|
|
Eoverflow:
|
|
|
|
m->op->stop(m, p);
|
2014-07-03 06:22:37 +08:00
|
|
|
kvfree(m->buf);
|
2013-11-19 09:20:43 +08:00
|
|
|
m->count = 0;
|
2014-07-03 06:22:37 +08:00
|
|
|
m->buf = seq_buf_alloc(m->size <<= 1);
|
2009-02-05 07:12:25 +08:00
|
|
|
return !m->buf ? -ENOMEM : -EAGAIN;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/**
|
|
|
|
* seq_read - ->read() method for sequential files.
|
2005-05-01 23:59:26 +08:00
|
|
|
* @file: the file to read from
|
|
|
|
* @buf: the buffer to read to
|
|
|
|
* @size: the maximum number of bytes to read
|
|
|
|
* @ppos: the current position in the file
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Ready-made ->f_op->read()
|
|
|
|
*/
|
|
|
|
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
|
|
|
|
{
|
2010-09-05 09:52:49 +08:00
|
|
|
struct seq_file *m = file->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
size_t copied = 0;
|
|
|
|
loff_t pos;
|
|
|
|
size_t n;
|
|
|
|
void *p;
|
|
|
|
int err = 0;
|
|
|
|
|
2006-03-23 19:00:37 +08:00
|
|
|
mutex_lock(&m->lock);
|
2009-02-19 06:48:16 +08:00
|
|
|
|
seq_file: fix mishandling of consecutive pread() invocations.
The following program illustrates the problem:
char buf[8192];
int fd = open("/proc/self/maps", O_RDONLY);
n = pread(fd, buf, sizeof(buf), 0);
printf("%d\n", n);
/* lseek(fd, 0, SEEK_CUR); */ /* Uncomment to work around */
n = pread(fd, buf, sizeof(buf), 0);
printf("%d\n", n);
The second printf() prints zero, but uncommenting the lseek() corrects its
behaviour.
To fix, make seq_read() mirror seq_lseek() when processing changes in
*ppos. Restore m->version first, then if required traverse and update
read_pos on success.
Addresses https://bugzilla.kernel.org/show_bug.cgi?id=11856
Signed-off-by: Earl Chew <echew@ixiacom.com>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 07:33:43 +08:00
|
|
|
/*
|
|
|
|
* seq_file->op->..m_start/m_stop/m_next may do special actions
|
|
|
|
* or optimisations based on the file->f_version, so we want to
|
|
|
|
* pass the file->f_version to those methods.
|
|
|
|
*
|
|
|
|
* seq_file->version is just copy of f_version, and seq_file
|
|
|
|
* methods can treat it simply as file version.
|
|
|
|
* It is copied in first and copied out after all operations.
|
|
|
|
* It is convenient to have it as part of structure to avoid the
|
|
|
|
* need of passing another argument to all the seq_file methods.
|
|
|
|
*/
|
|
|
|
m->version = file->f_version;
|
|
|
|
|
2016-11-29 22:18:20 +08:00
|
|
|
/*
|
|
|
|
* if request is to read from zero offset, reset iterator to first
|
|
|
|
* record as it might have been already advanced by previous requests
|
|
|
|
*/
|
2017-11-15 18:34:58 +08:00
|
|
|
if (*ppos == 0) {
|
2016-11-29 22:18:20 +08:00
|
|
|
m->index = 0;
|
2017-11-15 18:34:58 +08:00
|
|
|
m->version = 0;
|
|
|
|
m->count = 0;
|
|
|
|
}
|
2016-11-29 22:18:20 +08:00
|
|
|
|
2009-02-19 06:48:16 +08:00
|
|
|
/* Don't assume *ppos is where we left it */
|
|
|
|
if (unlikely(*ppos != m->read_pos)) {
|
|
|
|
while ((err = traverse(m, *ppos)) == -EAGAIN)
|
|
|
|
;
|
|
|
|
if (err) {
|
|
|
|
/* With prejudice... */
|
|
|
|
m->read_pos = 0;
|
|
|
|
m->version = 0;
|
|
|
|
m->index = 0;
|
|
|
|
m->count = 0;
|
|
|
|
goto Done;
|
seq_file: fix mishandling of consecutive pread() invocations.
The following program illustrates the problem:
char buf[8192];
int fd = open("/proc/self/maps", O_RDONLY);
n = pread(fd, buf, sizeof(buf), 0);
printf("%d\n", n);
/* lseek(fd, 0, SEEK_CUR); */ /* Uncomment to work around */
n = pread(fd, buf, sizeof(buf), 0);
printf("%d\n", n);
The second printf() prints zero, but uncommenting the lseek() corrects its
behaviour.
To fix, make seq_read() mirror seq_lseek() when processing changes in
*ppos. Restore m->version first, then if required traverse and update
read_pos on success.
Addresses https://bugzilla.kernel.org/show_bug.cgi?id=11856
Signed-off-by: Earl Chew <echew@ixiacom.com>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 07:33:43 +08:00
|
|
|
} else {
|
|
|
|
m->read_pos = *ppos;
|
2009-02-19 06:48:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* grab buffer if we didn't have one */
|
|
|
|
if (!m->buf) {
|
2014-07-03 06:22:37 +08:00
|
|
|
m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!m->buf)
|
|
|
|
goto Enomem;
|
|
|
|
}
|
|
|
|
/* if not empty - flush it first */
|
|
|
|
if (m->count) {
|
|
|
|
n = min(m->count, size);
|
|
|
|
err = copy_to_user(buf, m->buf + m->from, n);
|
|
|
|
if (err)
|
|
|
|
goto Efault;
|
|
|
|
m->count -= n;
|
|
|
|
m->from += n;
|
|
|
|
size -= n;
|
|
|
|
buf += n;
|
|
|
|
copied += n;
|
fs/seq_file: fix out-of-bounds read
seq_read() is a nasty piece of work, not to mention buggy.
It has (I think) an old bug which allows unprivileged userspace to read
beyond the end of m->buf.
I was getting these:
BUG: KASAN: slab-out-of-bounds in seq_read+0xcd2/0x1480 at addr ffff880116889880
Read of size 2713 by task trinity-c2/1329
CPU: 2 PID: 1329 Comm: trinity-c2 Not tainted 4.8.0-rc1+ #96
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.9.3-0-ge2fc41e-prebuilt.qemu-project.org 04/01/2014
Call Trace:
kasan_object_err+0x1c/0x80
kasan_report_error+0x2cb/0x7e0
kasan_report+0x4e/0x80
check_memory_region+0x13e/0x1a0
kasan_check_read+0x11/0x20
seq_read+0xcd2/0x1480
proc_reg_read+0x10b/0x260
do_loop_readv_writev.part.5+0x140/0x2c0
do_readv_writev+0x589/0x860
vfs_readv+0x7b/0xd0
do_readv+0xd8/0x2c0
SyS_readv+0xb/0x10
do_syscall_64+0x1b3/0x4b0
entry_SYSCALL64_slow_path+0x25/0x25
Object at ffff880116889100, in cache kmalloc-4096 size: 4096
Allocated:
PID = 1329
save_stack_trace+0x26/0x80
save_stack+0x46/0xd0
kasan_kmalloc+0xad/0xe0
__kmalloc+0x1aa/0x4a0
seq_buf_alloc+0x35/0x40
seq_read+0x7d8/0x1480
proc_reg_read+0x10b/0x260
do_loop_readv_writev.part.5+0x140/0x2c0
do_readv_writev+0x589/0x860
vfs_readv+0x7b/0xd0
do_readv+0xd8/0x2c0
SyS_readv+0xb/0x10
do_syscall_64+0x1b3/0x4b0
return_from_SYSCALL_64+0x0/0x6a
Freed:
PID = 0
(stack is not available)
Memory state around the buggy address:
ffff88011688a000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff88011688a080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88011688a100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
^
ffff88011688a180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88011688a200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
Disabling lock debugging due to kernel taint
This seems to be the same thing that Dave Jones was seeing here:
https://lkml.org/lkml/2016/8/12/334
There are multiple issues here:
1) If we enter the function with a non-empty buffer, there is an attempt
to flush it. But it was not clearing m->from after doing so, which
means that if we try to do this flush twice in a row without any call
to traverse() in between, we are going to be reading from the wrong
place -- the splat above, fixed by this patch.
2) If there's a short write to userspace because of page faults, the
buffer may already contain multiple lines (i.e. pos has advanced by
more than 1), but we don't save the progress that was made so the
next call will output what we've already returned previously. Since
that is a much less serious issue (and I have a headache after
staring at seq_read() for the past 8 hours), I'll leave that for now.
Link: http://lkml.kernel.org/r/1471447270-32093-1-git-send-email-vegard.nossum@oracle.com
Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com>
Reported-by: Dave Jones <davej@codemonkey.org.uk>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-08-26 06:17:11 +08:00
|
|
|
if (!m->count) {
|
|
|
|
m->from = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
m->index++;
|
fs/seq_file: fix out-of-bounds read
seq_read() is a nasty piece of work, not to mention buggy.
It has (I think) an old bug which allows unprivileged userspace to read
beyond the end of m->buf.
I was getting these:
BUG: KASAN: slab-out-of-bounds in seq_read+0xcd2/0x1480 at addr ffff880116889880
Read of size 2713 by task trinity-c2/1329
CPU: 2 PID: 1329 Comm: trinity-c2 Not tainted 4.8.0-rc1+ #96
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.9.3-0-ge2fc41e-prebuilt.qemu-project.org 04/01/2014
Call Trace:
kasan_object_err+0x1c/0x80
kasan_report_error+0x2cb/0x7e0
kasan_report+0x4e/0x80
check_memory_region+0x13e/0x1a0
kasan_check_read+0x11/0x20
seq_read+0xcd2/0x1480
proc_reg_read+0x10b/0x260
do_loop_readv_writev.part.5+0x140/0x2c0
do_readv_writev+0x589/0x860
vfs_readv+0x7b/0xd0
do_readv+0xd8/0x2c0
SyS_readv+0xb/0x10
do_syscall_64+0x1b3/0x4b0
entry_SYSCALL64_slow_path+0x25/0x25
Object at ffff880116889100, in cache kmalloc-4096 size: 4096
Allocated:
PID = 1329
save_stack_trace+0x26/0x80
save_stack+0x46/0xd0
kasan_kmalloc+0xad/0xe0
__kmalloc+0x1aa/0x4a0
seq_buf_alloc+0x35/0x40
seq_read+0x7d8/0x1480
proc_reg_read+0x10b/0x260
do_loop_readv_writev.part.5+0x140/0x2c0
do_readv_writev+0x589/0x860
vfs_readv+0x7b/0xd0
do_readv+0xd8/0x2c0
SyS_readv+0xb/0x10
do_syscall_64+0x1b3/0x4b0
return_from_SYSCALL_64+0x0/0x6a
Freed:
PID = 0
(stack is not available)
Memory state around the buggy address:
ffff88011688a000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff88011688a080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88011688a100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
^
ffff88011688a180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88011688a200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
Disabling lock debugging due to kernel taint
This seems to be the same thing that Dave Jones was seeing here:
https://lkml.org/lkml/2016/8/12/334
There are multiple issues here:
1) If we enter the function with a non-empty buffer, there is an attempt
to flush it. But it was not clearing m->from after doing so, which
means that if we try to do this flush twice in a row without any call
to traverse() in between, we are going to be reading from the wrong
place -- the splat above, fixed by this patch.
2) If there's a short write to userspace because of page faults, the
buffer may already contain multiple lines (i.e. pos has advanced by
more than 1), but we don't save the progress that was made so the
next call will output what we've already returned previously. Since
that is a much less serious issue (and I have a headache after
staring at seq_read() for the past 8 hours), I'll leave that for now.
Link: http://lkml.kernel.org/r/1471447270-32093-1-git-send-email-vegard.nossum@oracle.com
Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com>
Reported-by: Dave Jones <davej@codemonkey.org.uk>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-08-26 06:17:11 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!size)
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
/* we need at least one record in buffer */
|
2008-08-24 19:45:33 +08:00
|
|
|
pos = m->index;
|
|
|
|
p = m->op->start(m, &pos);
|
2005-04-17 06:20:36 +08:00
|
|
|
while (1) {
|
|
|
|
err = PTR_ERR(p);
|
|
|
|
if (!p || IS_ERR(p))
|
|
|
|
break;
|
|
|
|
err = m->op->show(m, p);
|
2008-03-28 12:46:41 +08:00
|
|
|
if (err < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2008-03-28 12:46:41 +08:00
|
|
|
if (unlikely(err))
|
|
|
|
m->count = 0;
|
2008-08-24 19:45:33 +08:00
|
|
|
if (unlikely(!m->count)) {
|
|
|
|
p = m->op->next(m, p, &pos);
|
|
|
|
m->index = pos;
|
|
|
|
continue;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (m->count < m->size)
|
|
|
|
goto Fill;
|
|
|
|
m->op->stop(m, p);
|
2014-07-03 06:22:37 +08:00
|
|
|
kvfree(m->buf);
|
2013-11-19 09:20:43 +08:00
|
|
|
m->count = 0;
|
2014-07-03 06:22:37 +08:00
|
|
|
m->buf = seq_buf_alloc(m->size <<= 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!m->buf)
|
|
|
|
goto Enomem;
|
|
|
|
m->version = 0;
|
2008-08-24 19:45:33 +08:00
|
|
|
pos = m->index;
|
|
|
|
p = m->op->start(m, &pos);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
m->op->stop(m, p);
|
|
|
|
m->count = 0;
|
|
|
|
goto Done;
|
|
|
|
Fill:
|
|
|
|
/* they want more? let's try to get some more */
|
|
|
|
while (m->count < size) {
|
|
|
|
size_t offs = m->count;
|
|
|
|
loff_t next = pos;
|
|
|
|
p = m->op->next(m, p, &next);
|
|
|
|
if (!p || IS_ERR(p)) {
|
|
|
|
err = PTR_ERR(p);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
err = m->op->show(m, p);
|
2014-09-30 07:08:21 +08:00
|
|
|
if (seq_has_overflowed(m) || err) {
|
2005-04-17 06:20:36 +08:00
|
|
|
m->count = offs;
|
2008-03-28 12:46:41 +08:00
|
|
|
if (likely(err <= 0))
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
pos = next;
|
|
|
|
}
|
|
|
|
m->op->stop(m, p);
|
|
|
|
n = min(m->count, size);
|
|
|
|
err = copy_to_user(buf, m->buf, n);
|
|
|
|
if (err)
|
|
|
|
goto Efault;
|
|
|
|
copied += n;
|
|
|
|
m->count -= n;
|
|
|
|
if (m->count)
|
|
|
|
m->from = n;
|
|
|
|
else
|
|
|
|
pos++;
|
|
|
|
m->index = pos;
|
|
|
|
Done:
|
|
|
|
if (!copied)
|
|
|
|
copied = err;
|
2009-02-19 06:48:16 +08:00
|
|
|
else {
|
2005-04-17 06:20:36 +08:00
|
|
|
*ppos += copied;
|
2009-02-19 06:48:16 +08:00
|
|
|
m->read_pos += copied;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
file->f_version = m->version;
|
2006-03-23 19:00:37 +08:00
|
|
|
mutex_unlock(&m->lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return copied;
|
|
|
|
Enomem:
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto Done;
|
|
|
|
Efault:
|
|
|
|
err = -EFAULT;
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_read);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_lseek - ->llseek() method for sequential files.
|
2005-05-01 23:59:26 +08:00
|
|
|
* @file: the file in question
|
|
|
|
* @offset: new position
|
2013-01-10 09:13:00 +08:00
|
|
|
* @whence: 0 for absolute, 1 for relative position
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Ready-made ->f_op->llseek()
|
|
|
|
*/
|
2012-12-18 07:59:39 +08:00
|
|
|
loff_t seq_lseek(struct file *file, loff_t offset, int whence)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-09-05 09:52:49 +08:00
|
|
|
struct seq_file *m = file->private_data;
|
2008-04-22 21:09:22 +08:00
|
|
|
loff_t retval = -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-03-23 19:00:37 +08:00
|
|
|
mutex_lock(&m->lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
m->version = file->f_version;
|
2012-12-18 07:59:39 +08:00
|
|
|
switch (whence) {
|
2013-02-28 09:03:22 +08:00
|
|
|
case SEEK_CUR:
|
|
|
|
offset += file->f_pos;
|
|
|
|
case SEEK_SET:
|
|
|
|
if (offset < 0)
|
|
|
|
break;
|
|
|
|
retval = offset;
|
|
|
|
if (offset != m->read_pos) {
|
|
|
|
while ((retval = traverse(m, offset)) == -EAGAIN)
|
|
|
|
;
|
|
|
|
if (retval) {
|
|
|
|
/* with extreme prejudice... */
|
|
|
|
file->f_pos = 0;
|
|
|
|
m->read_pos = 0;
|
|
|
|
m->version = 0;
|
|
|
|
m->index = 0;
|
|
|
|
m->count = 0;
|
|
|
|
} else {
|
|
|
|
m->read_pos = offset;
|
|
|
|
retval = file->f_pos = offset;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
seq_file: always update file->f_pos in seq_lseek()
This issue was first pointed out by Jiaxing Wang several months ago, but no
further comments:
https://lkml.org/lkml/2013/6/29/41
As we know pread() does not change f_pos, so after pread(), file->f_pos
and m->read_pos become different. And seq_lseek() does not update file->f_pos
if offset equals to m->read_pos, so after pread() and seq_lseek()(lseek to
m->read_pos), then a subsequent read may read from a wrong position, the
following program produces the problem:
char str1[32] = { 0 };
char str2[32] = { 0 };
int poffset = 10;
int count = 20;
/*open any seq file*/
int fd = open("/proc/modules", O_RDONLY);
pread(fd, str1, count, poffset);
printf("pread:%s\n", str1);
/*seek to where m->read_pos is*/
lseek(fd, poffset+count, SEEK_SET);
/*supposed to read from poffset+count, but this read from position 0*/
read(fd, str2, count);
printf("read:%s\n", str2);
out put:
pread:
ck_netbios_ns 12665
read:
nf_conntrack_netbios
/proc/modules:
nf_conntrack_netbios_ns 12665 0 - Live 0xffffffffa038b000
nf_conntrack_broadcast 12589 1 nf_conntrack_netbios_ns, Live 0xffffffffa0386000
So we always update file->f_pos to offset in seq_lseek() to fix this issue.
Signed-off-by: Jiaxing Wang <hello.wjx@gmail.com>
Signed-off-by: Gu Zheng <guz.fnst@cn.fujitsu.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2013-10-25 18:15:06 +08:00
|
|
|
} else {
|
|
|
|
file->f_pos = offset;
|
2013-02-28 09:03:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
file->f_version = m->version;
|
2007-07-16 14:40:22 +08:00
|
|
|
mutex_unlock(&m->lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_lseek);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_release - free the structures associated with sequential file.
|
|
|
|
* @file: file in question
|
2013-02-28 05:59:05 +08:00
|
|
|
* @inode: its inode
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Frees the structures associated with sequential file; can be used
|
|
|
|
* as ->f_op->release() if you don't have private data to destroy.
|
|
|
|
*/
|
|
|
|
int seq_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
2010-09-05 09:52:49 +08:00
|
|
|
struct seq_file *m = file->private_data;
|
2014-07-03 06:22:37 +08:00
|
|
|
kvfree(m->buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(m);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_release);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_escape - print string into buffer, escaping some characters
|
|
|
|
* @m: target buffer
|
|
|
|
* @s: string
|
|
|
|
* @esc: set of characters that need escaping
|
|
|
|
*
|
|
|
|
* Puts string into buffer, replacing each occurrence of character from
|
2015-09-12 04:07:48 +08:00
|
|
|
* @esc with usual octal escape.
|
|
|
|
* Use seq_has_overflowed() to check for errors.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2015-09-12 04:07:48 +08:00
|
|
|
void seq_escape(struct seq_file *m, const char *s, const char *esc)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-11-07 08:32:40 +08:00
|
|
|
char *buf;
|
|
|
|
size_t size = seq_get_buf(m, &buf);
|
|
|
|
int ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-11-07 08:32:40 +08:00
|
|
|
ret = string_escape_str(s, buf, size, ESCAPE_OCTAL, esc);
|
|
|
|
seq_commit(m, ret < size ? ret : -1);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_escape);
|
|
|
|
|
2015-09-12 04:07:48 +08:00
|
|
|
void seq_vprintf(struct seq_file *m, const char *f, va_list args)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (m->count < m->size) {
|
|
|
|
len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
|
|
|
|
if (m->count + len < m->size) {
|
|
|
|
m->count += len;
|
2015-09-12 04:07:48 +08:00
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2012-03-24 06:02:55 +08:00
|
|
|
seq_set_overflow(m);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-06-11 20:16:35 +08:00
|
|
|
EXPORT_SYMBOL(seq_vprintf);
|
|
|
|
|
2015-09-12 04:07:48 +08:00
|
|
|
void seq_printf(struct seq_file *m, const char *f, ...)
|
2012-06-11 20:16:35 +08:00
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, f);
|
2015-09-12 04:07:48 +08:00
|
|
|
seq_vprintf(m, f, args);
|
2012-06-11 20:16:35 +08:00
|
|
|
va_end(args);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
EXPORT_SYMBOL(seq_printf);
|
|
|
|
|
2008-11-22 19:28:48 +08:00
|
|
|
/**
|
2008-11-24 05:24:53 +08:00
|
|
|
* mangle_path - mangle and copy path to buffer beginning
|
|
|
|
* @s: buffer start
|
|
|
|
* @p: beginning of path in above buffer
|
|
|
|
* @esc: set of characters that need escaping
|
2008-11-22 19:28:48 +08:00
|
|
|
*
|
|
|
|
* Copy the path from @p to @s, replacing each occurrence of character from
|
|
|
|
* @esc with usual octal escape.
|
|
|
|
* Returns pointer past last written character in @s, or NULL in case of
|
|
|
|
* failure.
|
|
|
|
*/
|
2011-12-09 09:18:57 +08:00
|
|
|
char *mangle_path(char *s, const char *p, const char *esc)
|
2008-03-27 20:06:20 +08:00
|
|
|
{
|
|
|
|
while (s <= p) {
|
|
|
|
char c = *p++;
|
|
|
|
if (!c) {
|
|
|
|
return s;
|
|
|
|
} else if (!strchr(esc, c)) {
|
|
|
|
*s++ = c;
|
|
|
|
} else if (s + 4 > p) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
*s++ = '\\';
|
|
|
|
*s++ = '0' + ((c & 0300) >> 6);
|
|
|
|
*s++ = '0' + ((c & 070) >> 3);
|
|
|
|
*s++ = '0' + (c & 07);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-11-29 01:03:22 +08:00
|
|
|
EXPORT_SYMBOL(mangle_path);
|
2008-03-27 20:06:20 +08:00
|
|
|
|
2008-12-02 06:35:00 +08:00
|
|
|
/**
|
|
|
|
* seq_path - seq_file interface to print a pathname
|
|
|
|
* @m: the seq_file handle
|
|
|
|
* @path: the struct path to print
|
|
|
|
* @esc: set of characters to escape in the output
|
|
|
|
*
|
|
|
|
* return the absolute path of 'path', as represented by the
|
|
|
|
* dentry / mnt pair in the path parameter.
|
2008-03-27 20:06:20 +08:00
|
|
|
*/
|
2011-12-09 09:18:57 +08:00
|
|
|
int seq_path(struct seq_file *m, const struct path *path, const char *esc)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-09-21 20:48:36 +08:00
|
|
|
char *buf;
|
|
|
|
size_t size = seq_get_buf(m, &buf);
|
|
|
|
int res = -1;
|
|
|
|
|
|
|
|
if (size) {
|
|
|
|
char *p = d_path(path, buf, size);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!IS_ERR(p)) {
|
2009-09-21 20:48:36 +08:00
|
|
|
char *end = mangle_path(buf, p, esc);
|
|
|
|
if (end)
|
|
|
|
res = end - buf;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-21 20:48:36 +08:00
|
|
|
seq_commit(m, res);
|
|
|
|
|
|
|
|
return res;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_path);
|
|
|
|
|
2015-06-19 16:30:28 +08:00
|
|
|
/**
|
|
|
|
* seq_file_path - seq_file interface to print a pathname of a file
|
|
|
|
* @m: the seq_file handle
|
|
|
|
* @file: the struct file to print
|
|
|
|
* @esc: set of characters to escape in the output
|
|
|
|
*
|
|
|
|
* return the absolute path to the file.
|
|
|
|
*/
|
|
|
|
int seq_file_path(struct seq_file *m, struct file *file, const char *esc)
|
|
|
|
{
|
|
|
|
return seq_path(m, &file->f_path, esc);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_file_path);
|
|
|
|
|
2008-03-27 20:06:21 +08:00
|
|
|
/*
|
|
|
|
* Same as seq_path, but relative to supplied root.
|
|
|
|
*/
|
2011-12-09 09:18:57 +08:00
|
|
|
int seq_path_root(struct seq_file *m, const struct path *path,
|
|
|
|
const struct path *root, const char *esc)
|
2008-03-27 20:06:21 +08:00
|
|
|
{
|
2009-09-21 20:48:36 +08:00
|
|
|
char *buf;
|
|
|
|
size_t size = seq_get_buf(m, &buf);
|
|
|
|
int res = -ENAMETOOLONG;
|
|
|
|
|
|
|
|
if (size) {
|
2008-03-27 20:06:21 +08:00
|
|
|
char *p;
|
|
|
|
|
2009-09-21 20:48:36 +08:00
|
|
|
p = __d_path(path, root, buf, size);
|
fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API
__d_path() API is asking for trouble and in case of apparmor d_namespace_path()
getting just that. The root cause is that when __d_path() misses the root
it had been told to look for, it stores the location of the most remote ancestor
in *root. Without grabbing references. Sure, at the moment of call it had
been pinned down by what we have in *path. And if we raced with umount -l, we
could have very well stopped at vfsmount/dentry that got freed as soon as
prepend_path() dropped vfsmount_lock.
It is safe to compare these pointers with pre-existing (and known to be still
alive) vfsmount and dentry, as long as all we are asking is "is it the same
address?". Dereferencing is not safe and apparmor ended up stepping into
that. d_namespace_path() really wants to examine the place where we stopped,
even if it's not connected to our namespace. As the result, it looked
at ->d_sb->s_magic of a dentry that might've been already freed by that point.
All other callers had been careful enough to avoid that, but it's really
a bad interface - it invites that kind of trouble.
The fix is fairly straightforward, even though it's bigger than I'd like:
* prepend_path() root argument becomes const.
* __d_path() is never called with NULL/NULL root. It was a kludge
to start with. Instead, we have an explicit function - d_absolute_root().
Same as __d_path(), except that it doesn't get root passed and stops where
it stops. apparmor and tomoyo are using it.
* __d_path() returns NULL on path outside of root. The main
caller is show_mountinfo() and that's precisely what we pass root for - to
skip those outside chroot jail. Those who don't want that can (and do)
use d_path().
* __d_path() root argument becomes const. Everyone agrees, I hope.
* apparmor does *NOT* try to use __d_path() or any of its variants
when it sees that path->mnt is an internal vfsmount. In that case it's
definitely not mounted anywhere and dentry_path() is exactly what we want
there. Handling of sysctl()-triggered weirdness is moved to that place.
* if apparmor is asked to do pathname relative to chroot jail
and __d_path() tells it we it's not in that jail, the sucker just calls
d_absolute_path() instead. That's the other remaining caller of __d_path(),
BTW.
* seq_path_root() does _NOT_ return -ENAMETOOLONG (it's stupid anyway -
the normal seq_file logics will take care of growing the buffer and redoing
the call of ->show() just fine). However, if it gets path not reachable
from root, it returns SEQ_SKIP. The only caller adjusted (i.e. stopped
ignoring the return value as it used to do).
Reviewed-by: John Johansen <john.johansen@canonical.com>
ACKed-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: stable@vger.kernel.org
2011-12-05 21:43:34 +08:00
|
|
|
if (!p)
|
|
|
|
return SEQ_SKIP;
|
2009-09-21 20:48:36 +08:00
|
|
|
res = PTR_ERR(p);
|
2008-03-27 20:06:21 +08:00
|
|
|
if (!IS_ERR(p)) {
|
2009-09-21 20:48:36 +08:00
|
|
|
char *end = mangle_path(buf, p, esc);
|
|
|
|
if (end)
|
|
|
|
res = end - buf;
|
|
|
|
else
|
|
|
|
res = -ENAMETOOLONG;
|
2008-03-27 20:06:21 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-21 20:48:36 +08:00
|
|
|
seq_commit(m, res);
|
|
|
|
|
fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API
__d_path() API is asking for trouble and in case of apparmor d_namespace_path()
getting just that. The root cause is that when __d_path() misses the root
it had been told to look for, it stores the location of the most remote ancestor
in *root. Without grabbing references. Sure, at the moment of call it had
been pinned down by what we have in *path. And if we raced with umount -l, we
could have very well stopped at vfsmount/dentry that got freed as soon as
prepend_path() dropped vfsmount_lock.
It is safe to compare these pointers with pre-existing (and known to be still
alive) vfsmount and dentry, as long as all we are asking is "is it the same
address?". Dereferencing is not safe and apparmor ended up stepping into
that. d_namespace_path() really wants to examine the place where we stopped,
even if it's not connected to our namespace. As the result, it looked
at ->d_sb->s_magic of a dentry that might've been already freed by that point.
All other callers had been careful enough to avoid that, but it's really
a bad interface - it invites that kind of trouble.
The fix is fairly straightforward, even though it's bigger than I'd like:
* prepend_path() root argument becomes const.
* __d_path() is never called with NULL/NULL root. It was a kludge
to start with. Instead, we have an explicit function - d_absolute_root().
Same as __d_path(), except that it doesn't get root passed and stops where
it stops. apparmor and tomoyo are using it.
* __d_path() returns NULL on path outside of root. The main
caller is show_mountinfo() and that's precisely what we pass root for - to
skip those outside chroot jail. Those who don't want that can (and do)
use d_path().
* __d_path() root argument becomes const. Everyone agrees, I hope.
* apparmor does *NOT* try to use __d_path() or any of its variants
when it sees that path->mnt is an internal vfsmount. In that case it's
definitely not mounted anywhere and dentry_path() is exactly what we want
there. Handling of sysctl()-triggered weirdness is moved to that place.
* if apparmor is asked to do pathname relative to chroot jail
and __d_path() tells it we it's not in that jail, the sucker just calls
d_absolute_path() instead. That's the other remaining caller of __d_path(),
BTW.
* seq_path_root() does _NOT_ return -ENAMETOOLONG (it's stupid anyway -
the normal seq_file logics will take care of growing the buffer and redoing
the call of ->show() just fine). However, if it gets path not reachable
from root, it returns SEQ_SKIP. The only caller adjusted (i.e. stopped
ignoring the return value as it used to do).
Reviewed-by: John Johansen <john.johansen@canonical.com>
ACKed-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: stable@vger.kernel.org
2011-12-05 21:43:34 +08:00
|
|
|
return res < 0 && res != -ENAMETOOLONG ? res : 0;
|
2008-03-27 20:06:21 +08:00
|
|
|
}
|
|
|
|
|
2008-03-27 20:06:20 +08:00
|
|
|
/*
|
|
|
|
* returns the path of the 'dentry' from the root of its filesystem.
|
|
|
|
*/
|
2011-12-09 09:18:57 +08:00
|
|
|
int seq_dentry(struct seq_file *m, struct dentry *dentry, const char *esc)
|
2008-03-27 20:06:20 +08:00
|
|
|
{
|
2009-09-21 20:48:36 +08:00
|
|
|
char *buf;
|
|
|
|
size_t size = seq_get_buf(m, &buf);
|
|
|
|
int res = -1;
|
|
|
|
|
|
|
|
if (size) {
|
|
|
|
char *p = dentry_path(dentry, buf, size);
|
2008-03-27 20:06:20 +08:00
|
|
|
if (!IS_ERR(p)) {
|
2009-09-21 20:48:36 +08:00
|
|
|
char *end = mangle_path(buf, p, esc);
|
|
|
|
if (end)
|
|
|
|
res = end - buf;
|
2008-03-27 20:06:20 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-21 20:48:36 +08:00
|
|
|
seq_commit(m, res);
|
|
|
|
|
|
|
|
return res;
|
2008-03-27 20:06:20 +08:00
|
|
|
}
|
2015-05-18 17:16:31 +08:00
|
|
|
EXPORT_SYMBOL(seq_dentry);
|
2008-03-27 20:06:20 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void *single_start(struct seq_file *p, loff_t *pos)
|
|
|
|
{
|
|
|
|
return NULL + (*pos == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *single_next(struct seq_file *p, void *v, loff_t *pos)
|
|
|
|
{
|
|
|
|
++*pos;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void single_stop(struct seq_file *p, void *v)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int single_open(struct file *file, int (*show)(struct seq_file *, void *),
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
|
|
|
|
int res = -ENOMEM;
|
|
|
|
|
|
|
|
if (op) {
|
|
|
|
op->start = single_start;
|
|
|
|
op->next = single_next;
|
|
|
|
op->stop = single_stop;
|
|
|
|
op->show = show;
|
|
|
|
res = seq_open(file, op);
|
|
|
|
if (!res)
|
|
|
|
((struct seq_file *)file->private_data)->private = data;
|
|
|
|
else
|
|
|
|
kfree(op);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(single_open);
|
|
|
|
|
2013-04-01 01:43:23 +08:00
|
|
|
int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),
|
|
|
|
void *data, size_t size)
|
|
|
|
{
|
2014-07-03 06:22:37 +08:00
|
|
|
char *buf = seq_buf_alloc(size);
|
2013-04-01 01:43:23 +08:00
|
|
|
int ret;
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = single_open(file, show, data);
|
|
|
|
if (ret) {
|
2014-07-03 06:22:37 +08:00
|
|
|
kvfree(buf);
|
2013-04-01 01:43:23 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
((struct seq_file *)file->private_data)->buf = buf;
|
|
|
|
((struct seq_file *)file->private_data)->size = size;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(single_open_size);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int single_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
2006-12-07 12:40:36 +08:00
|
|
|
const struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
|
2005-04-17 06:20:36 +08:00
|
|
|
int res = seq_release(inode, file);
|
|
|
|
kfree(op);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(single_release);
|
|
|
|
|
|
|
|
int seq_release_private(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
struct seq_file *seq = file->private_data;
|
|
|
|
|
|
|
|
kfree(seq->private);
|
|
|
|
seq->private = NULL;
|
|
|
|
return seq_release(inode, file);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_release_private);
|
|
|
|
|
2007-10-10 17:28:42 +08:00
|
|
|
void *__seq_open_private(struct file *f, const struct seq_operations *ops,
|
|
|
|
int psize)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
void *private;
|
|
|
|
struct seq_file *seq;
|
|
|
|
|
|
|
|
private = kzalloc(psize, GFP_KERNEL);
|
|
|
|
if (private == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
rc = seq_open(f, ops);
|
|
|
|
if (rc < 0)
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
seq = f->private_data;
|
|
|
|
seq->private = private;
|
|
|
|
return private;
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
kfree(private);
|
|
|
|
out:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__seq_open_private);
|
|
|
|
|
|
|
|
int seq_open_private(struct file *filp, const struct seq_operations *ops,
|
|
|
|
int psize)
|
|
|
|
{
|
|
|
|
return __seq_open_private(filp, ops, psize) ? 0 : -ENOMEM;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_open_private);
|
|
|
|
|
2015-09-12 04:07:48 +08:00
|
|
|
void seq_putc(struct seq_file *m, char c)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-09-12 04:07:48 +08:00
|
|
|
if (m->count >= m->size)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m->buf[m->count++] = c;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_putc);
|
|
|
|
|
2015-09-12 04:07:48 +08:00
|
|
|
void seq_puts(struct seq_file *m, const char *s)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int len = strlen(s);
|
2015-09-12 04:07:48 +08:00
|
|
|
|
|
|
|
if (m->count + len >= m->size) {
|
|
|
|
seq_set_overflow(m);
|
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2015-09-12 04:07:48 +08:00
|
|
|
memcpy(m->buf + m->count, s, len);
|
|
|
|
m->count += len;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_puts);
|
2007-07-11 08:22:26 +08:00
|
|
|
|
2012-03-24 06:02:54 +08:00
|
|
|
/*
|
|
|
|
* A helper routine for putting decimal numbers without rich format of printf().
|
|
|
|
* only 'unsigned long long' is supported.
|
2016-10-08 08:02:20 +08:00
|
|
|
* This routine will put strlen(delimiter) + number into seq_file.
|
2012-03-24 06:02:54 +08:00
|
|
|
* This routine is very quick when you show lots of numbers.
|
|
|
|
* In usual cases, it will be better to use seq_printf(). It's easier to read.
|
|
|
|
*/
|
2016-10-08 08:02:20 +08:00
|
|
|
void seq_put_decimal_ull(struct seq_file *m, const char *delimiter,
|
2015-09-12 04:07:48 +08:00
|
|
|
unsigned long long num)
|
2012-03-24 06:02:54 +08:00
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (m->count + 2 >= m->size) /* we'll write 2 bytes at least */
|
|
|
|
goto overflow;
|
|
|
|
|
2016-10-08 08:02:20 +08:00
|
|
|
len = strlen(delimiter);
|
|
|
|
if (m->count + len >= m->size)
|
|
|
|
goto overflow;
|
|
|
|
|
|
|
|
memcpy(m->buf + m->count, delimiter, len);
|
|
|
|
m->count += len;
|
|
|
|
|
|
|
|
if (m->count + 1 >= m->size)
|
|
|
|
goto overflow;
|
2012-03-24 06:02:54 +08:00
|
|
|
|
|
|
|
if (num < 10) {
|
|
|
|
m->buf[m->count++] = num + '0';
|
2015-09-12 04:07:48 +08:00
|
|
|
return;
|
2012-03-24 06:02:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
len = num_to_str(m->buf + m->count, m->size - m->count, num);
|
|
|
|
if (!len)
|
|
|
|
goto overflow;
|
2016-10-08 08:02:20 +08:00
|
|
|
|
2012-03-24 06:02:54 +08:00
|
|
|
m->count += len;
|
2015-09-12 04:07:48 +08:00
|
|
|
return;
|
|
|
|
|
2012-03-24 06:02:54 +08:00
|
|
|
overflow:
|
2012-03-24 06:02:55 +08:00
|
|
|
seq_set_overflow(m);
|
2012-03-24 06:02:54 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_put_decimal_ull);
|
|
|
|
|
2016-10-08 08:02:20 +08:00
|
|
|
void seq_put_decimal_ll(struct seq_file *m, const char *delimiter, long long num)
|
2012-03-24 06:02:54 +08:00
|
|
|
{
|
2016-10-08 08:02:20 +08:00
|
|
|
int len;
|
|
|
|
|
|
|
|
if (m->count + 3 >= m->size) /* we'll write 2 bytes at least */
|
|
|
|
goto overflow;
|
|
|
|
|
|
|
|
len = strlen(delimiter);
|
|
|
|
if (m->count + len >= m->size)
|
|
|
|
goto overflow;
|
|
|
|
|
|
|
|
memcpy(m->buf + m->count, delimiter, len);
|
|
|
|
m->count += len;
|
|
|
|
|
|
|
|
if (m->count + 2 >= m->size)
|
|
|
|
goto overflow;
|
|
|
|
|
2012-03-24 06:02:54 +08:00
|
|
|
if (num < 0) {
|
2016-10-08 08:02:20 +08:00
|
|
|
m->buf[m->count++] = '-';
|
2012-03-24 06:02:54 +08:00
|
|
|
num = -num;
|
|
|
|
}
|
2016-10-08 08:02:20 +08:00
|
|
|
|
|
|
|
if (num < 10) {
|
|
|
|
m->buf[m->count++] = num + '0';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = num_to_str(m->buf + m->count, m->size - m->count, num);
|
|
|
|
if (!len)
|
|
|
|
goto overflow;
|
|
|
|
|
|
|
|
m->count += len;
|
|
|
|
return;
|
|
|
|
|
|
|
|
overflow:
|
|
|
|
seq_set_overflow(m);
|
2012-03-24 06:02:54 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_put_decimal_ll);
|
|
|
|
|
2009-06-18 07:28:05 +08:00
|
|
|
/**
|
|
|
|
* seq_write - write arbitrary data to buffer
|
|
|
|
* @seq: seq_file identifying the buffer to which data should be written
|
|
|
|
* @data: data address
|
|
|
|
* @len: number of bytes
|
|
|
|
*
|
|
|
|
* Return 0 on success, non-zero otherwise.
|
|
|
|
*/
|
|
|
|
int seq_write(struct seq_file *seq, const void *data, size_t len)
|
|
|
|
{
|
|
|
|
if (seq->count + len < seq->size) {
|
|
|
|
memcpy(seq->buf + seq->count, data, len);
|
|
|
|
seq->count += len;
|
|
|
|
return 0;
|
|
|
|
}
|
2012-03-24 06:02:55 +08:00
|
|
|
seq_set_overflow(seq);
|
2009-06-18 07:28:05 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_write);
|
|
|
|
|
2013-11-15 06:31:56 +08:00
|
|
|
/**
|
|
|
|
* seq_pad - write padding spaces to buffer
|
|
|
|
* @m: seq_file identifying the buffer to which data should be written
|
|
|
|
* @c: the byte to append after padding if non-zero
|
|
|
|
*/
|
|
|
|
void seq_pad(struct seq_file *m, char c)
|
|
|
|
{
|
|
|
|
int size = m->pad_until - m->count;
|
|
|
|
if (size > 0)
|
|
|
|
seq_printf(m, "%*s", size, "");
|
|
|
|
if (c)
|
|
|
|
seq_putc(m, c);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_pad);
|
|
|
|
|
2015-09-10 06:38:33 +08:00
|
|
|
/* A complete analogue of print_hex_dump() */
|
|
|
|
void seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,
|
|
|
|
int rowsize, int groupsize, const void *buf, size_t len,
|
|
|
|
bool ascii)
|
|
|
|
{
|
|
|
|
const u8 *ptr = buf;
|
|
|
|
int i, linelen, remaining = len;
|
2015-11-07 08:32:37 +08:00
|
|
|
char *buffer;
|
|
|
|
size_t size;
|
2015-09-10 06:38:33 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (rowsize != 16 && rowsize != 32)
|
|
|
|
rowsize = 16;
|
|
|
|
|
|
|
|
for (i = 0; i < len && !seq_has_overflowed(m); i += rowsize) {
|
|
|
|
linelen = min(remaining, rowsize);
|
|
|
|
remaining -= rowsize;
|
|
|
|
|
|
|
|
switch (prefix_type) {
|
|
|
|
case DUMP_PREFIX_ADDRESS:
|
|
|
|
seq_printf(m, "%s%p: ", prefix_str, ptr + i);
|
|
|
|
break;
|
|
|
|
case DUMP_PREFIX_OFFSET:
|
|
|
|
seq_printf(m, "%s%.8x: ", prefix_str, i);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
seq_printf(m, "%s", prefix_str);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-11-07 08:32:37 +08:00
|
|
|
size = seq_get_buf(m, &buffer);
|
2015-09-10 06:38:33 +08:00
|
|
|
ret = hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
|
2015-11-07 08:32:37 +08:00
|
|
|
buffer, size, ascii);
|
|
|
|
seq_commit(m, ret < size ? ret : -1);
|
|
|
|
|
|
|
|
seq_putc(m, '\n');
|
2015-09-10 06:38:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hex_dump);
|
|
|
|
|
2007-07-11 08:22:26 +08:00
|
|
|
struct list_head *seq_list_start(struct list_head *head, loff_t pos)
|
|
|
|
{
|
|
|
|
struct list_head *lh;
|
|
|
|
|
|
|
|
list_for_each(lh, head)
|
|
|
|
if (pos-- == 0)
|
|
|
|
return lh;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_list_start);
|
|
|
|
|
|
|
|
struct list_head *seq_list_start_head(struct list_head *head, loff_t pos)
|
|
|
|
{
|
|
|
|
if (!pos)
|
|
|
|
return head;
|
|
|
|
|
|
|
|
return seq_list_start(head, pos - 1);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_list_start_head);
|
|
|
|
|
|
|
|
struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct list_head *lh;
|
|
|
|
|
|
|
|
lh = ((struct list_head *)v)->next;
|
|
|
|
++*ppos;
|
|
|
|
return lh == head ? NULL : lh;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_list_next);
|
2010-02-09 07:18:22 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_hlist_start - start an iteration of a hlist
|
|
|
|
* @head: the head of the hlist
|
|
|
|
* @pos: the start position of the sequence
|
|
|
|
*
|
|
|
|
* Called at seq_file->op->start().
|
|
|
|
*/
|
|
|
|
struct hlist_node *seq_hlist_start(struct hlist_head *head, loff_t pos)
|
|
|
|
{
|
|
|
|
struct hlist_node *node;
|
|
|
|
|
|
|
|
hlist_for_each(node, head)
|
|
|
|
if (pos-- == 0)
|
|
|
|
return node;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hlist_start);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_hlist_start_head - start an iteration of a hlist
|
|
|
|
* @head: the head of the hlist
|
|
|
|
* @pos: the start position of the sequence
|
|
|
|
*
|
|
|
|
* Called at seq_file->op->start(). Call this function if you want to
|
|
|
|
* print a header at the top of the output.
|
|
|
|
*/
|
|
|
|
struct hlist_node *seq_hlist_start_head(struct hlist_head *head, loff_t pos)
|
|
|
|
{
|
|
|
|
if (!pos)
|
|
|
|
return SEQ_START_TOKEN;
|
|
|
|
|
|
|
|
return seq_hlist_start(head, pos - 1);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hlist_start_head);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_hlist_next - move to the next position of the hlist
|
|
|
|
* @v: the current iterator
|
|
|
|
* @head: the head of the hlist
|
2010-03-05 01:37:12 +08:00
|
|
|
* @ppos: the current position
|
2010-02-09 07:18:22 +08:00
|
|
|
*
|
|
|
|
* Called at seq_file->op->next().
|
|
|
|
*/
|
|
|
|
struct hlist_node *seq_hlist_next(void *v, struct hlist_head *head,
|
|
|
|
loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct hlist_node *node = v;
|
|
|
|
|
|
|
|
++*ppos;
|
|
|
|
if (v == SEQ_START_TOKEN)
|
|
|
|
return head->first;
|
|
|
|
else
|
|
|
|
return node->next;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hlist_next);
|
2010-02-22 15:57:17 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_hlist_start_rcu - start an iteration of a hlist protected by RCU
|
|
|
|
* @head: the head of the hlist
|
|
|
|
* @pos: the start position of the sequence
|
|
|
|
*
|
|
|
|
* Called at seq_file->op->start().
|
|
|
|
*
|
|
|
|
* This list-traversal primitive may safely run concurrently with
|
|
|
|
* the _rcu list-mutation primitives such as hlist_add_head_rcu()
|
|
|
|
* as long as the traversal is guarded by rcu_read_lock().
|
|
|
|
*/
|
|
|
|
struct hlist_node *seq_hlist_start_rcu(struct hlist_head *head,
|
|
|
|
loff_t pos)
|
|
|
|
{
|
|
|
|
struct hlist_node *node;
|
|
|
|
|
|
|
|
__hlist_for_each_rcu(node, head)
|
|
|
|
if (pos-- == 0)
|
|
|
|
return node;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hlist_start_rcu);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_hlist_start_head_rcu - start an iteration of a hlist protected by RCU
|
|
|
|
* @head: the head of the hlist
|
|
|
|
* @pos: the start position of the sequence
|
|
|
|
*
|
|
|
|
* Called at seq_file->op->start(). Call this function if you want to
|
|
|
|
* print a header at the top of the output.
|
|
|
|
*
|
|
|
|
* This list-traversal primitive may safely run concurrently with
|
|
|
|
* the _rcu list-mutation primitives such as hlist_add_head_rcu()
|
|
|
|
* as long as the traversal is guarded by rcu_read_lock().
|
|
|
|
*/
|
|
|
|
struct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head,
|
|
|
|
loff_t pos)
|
|
|
|
{
|
|
|
|
if (!pos)
|
|
|
|
return SEQ_START_TOKEN;
|
|
|
|
|
|
|
|
return seq_hlist_start_rcu(head, pos - 1);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hlist_start_head_rcu);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_hlist_next_rcu - move to the next position of the hlist protected by RCU
|
|
|
|
* @v: the current iterator
|
|
|
|
* @head: the head of the hlist
|
2010-03-05 01:37:12 +08:00
|
|
|
* @ppos: the current position
|
2010-02-22 15:57:17 +08:00
|
|
|
*
|
|
|
|
* Called at seq_file->op->next().
|
|
|
|
*
|
|
|
|
* This list-traversal primitive may safely run concurrently with
|
|
|
|
* the _rcu list-mutation primitives such as hlist_add_head_rcu()
|
|
|
|
* as long as the traversal is guarded by rcu_read_lock().
|
|
|
|
*/
|
|
|
|
struct hlist_node *seq_hlist_next_rcu(void *v,
|
|
|
|
struct hlist_head *head,
|
|
|
|
loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct hlist_node *node = v;
|
|
|
|
|
|
|
|
++*ppos;
|
|
|
|
if (v == SEQ_START_TOKEN)
|
|
|
|
return rcu_dereference(head->first);
|
|
|
|
else
|
|
|
|
return rcu_dereference(node->next);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hlist_next_rcu);
|
2013-06-21 20:58:21 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_hlist_start_precpu - start an iteration of a percpu hlist array
|
|
|
|
* @head: pointer to percpu array of struct hlist_heads
|
|
|
|
* @cpu: pointer to cpu "cursor"
|
|
|
|
* @pos: start position of sequence
|
|
|
|
*
|
|
|
|
* Called at seq_file->op->start().
|
|
|
|
*/
|
|
|
|
struct hlist_node *
|
|
|
|
seq_hlist_start_percpu(struct hlist_head __percpu *head, int *cpu, loff_t pos)
|
|
|
|
{
|
|
|
|
struct hlist_node *node;
|
|
|
|
|
|
|
|
for_each_possible_cpu(*cpu) {
|
|
|
|
hlist_for_each(node, per_cpu_ptr(head, *cpu)) {
|
|
|
|
if (pos-- == 0)
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hlist_start_percpu);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* seq_hlist_next_percpu - move to the next position of the percpu hlist array
|
|
|
|
* @v: pointer to current hlist_node
|
|
|
|
* @head: pointer to percpu array of struct hlist_heads
|
|
|
|
* @cpu: pointer to cpu "cursor"
|
|
|
|
* @pos: start position of sequence
|
|
|
|
*
|
|
|
|
* Called at seq_file->op->next().
|
|
|
|
*/
|
|
|
|
struct hlist_node *
|
|
|
|
seq_hlist_next_percpu(void *v, struct hlist_head __percpu *head,
|
|
|
|
int *cpu, loff_t *pos)
|
|
|
|
{
|
|
|
|
struct hlist_node *node = v;
|
|
|
|
|
|
|
|
++*pos;
|
|
|
|
|
|
|
|
if (node->next)
|
|
|
|
return node->next;
|
|
|
|
|
|
|
|
for (*cpu = cpumask_next(*cpu, cpu_possible_mask); *cpu < nr_cpu_ids;
|
|
|
|
*cpu = cpumask_next(*cpu, cpu_possible_mask)) {
|
|
|
|
struct hlist_head *bucket = per_cpu_ptr(head, *cpu);
|
|
|
|
|
|
|
|
if (!hlist_empty(bucket))
|
|
|
|
return bucket->first;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(seq_hlist_next_percpu);
|