|
|
9fc0f6 |
From 035c9e559064114e3f7ba19b593a97c4a4d4f060 Mon Sep 17 00:00:00 2001
|
|
|
9fc0f6 |
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
|
|
9fc0f6 |
Date: Sat, 28 Dec 2013 19:33:23 -0500
|
|
|
9fc0f6 |
Subject: [PATCH] journal: fix access to munmapped memory in
|
|
|
9fc0f6 |
sd_journal_enumerate_unique
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
sd_j_e_u needs to keep a reference to an object while comparing it
|
|
|
9fc0f6 |
with possibly duplicate objects in other files. Because the size of
|
|
|
9fc0f6 |
mmap cache is limited, with enough files and object to compare to,
|
|
|
9fc0f6 |
at some point the object being compared would be munmapped, resulting
|
|
|
9fc0f6 |
in a segmentation fault.
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
Fix this issue by turning keep_always into a reference count that can
|
|
|
9fc0f6 |
be increased and decreased. Other callers which set keep_always=true
|
|
|
9fc0f6 |
are unmodified: their references are never released but are ignored
|
|
|
9fc0f6 |
when the whole file is closed, which happens at some point. keep_always
|
|
|
9fc0f6 |
is increased in sd_j_e_u and later on released.
|
|
|
9fc0f6 |
---
|
|
|
9fc0f6 |
src/journal/journal-file.c | 5 +---
|
|
|
9fc0f6 |
src/journal/journal-file.h | 24 +++++++++++++++++++
|
|
|
9fc0f6 |
src/journal/journal-verify.c | 4 ----
|
|
|
9fc0f6 |
src/journal/mmap-cache.c | 57 +++++++++++++++++++++++++++++++++++---------
|
|
|
9fc0f6 |
src/journal/mmap-cache.h | 18 +++++++++++++-
|
|
|
9fc0f6 |
src/journal/sd-journal.c | 18 +++++++++++---
|
|
|
9fc0f6 |
6 files changed, 103 insertions(+), 23 deletions(-)
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
|
|
|
9fc0f6 |
index 748816a..9dbd674 100644
|
|
|
9fc0f6 |
--- a/src/journal/journal-file.c
|
|
|
9fc0f6 |
+++ b/src/journal/journal-file.c
|
|
|
9fc0f6 |
@@ -419,7 +419,6 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec
|
|
|
9fc0f6 |
void *t;
|
|
|
9fc0f6 |
Object *o;
|
|
|
9fc0f6 |
uint64_t s;
|
|
|
9fc0f6 |
- unsigned context;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
assert(f);
|
|
|
9fc0f6 |
assert(ret);
|
|
|
9fc0f6 |
@@ -428,10 +427,8 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec
|
|
|
9fc0f6 |
if (!VALID64(offset))
|
|
|
9fc0f6 |
return -EFAULT;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
- /* One context for each type, plus one catch-all for the rest */
|
|
|
9fc0f6 |
- context = type > 0 && type < _OBJECT_TYPE_MAX ? type : 0;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
- r = journal_file_move_to(f, context, false, offset, sizeof(ObjectHeader), &t);
|
|
|
9fc0f6 |
+ r = journal_file_move_to(f, type_to_context(type), false, offset, sizeof(ObjectHeader), &t);
|
|
|
9fc0f6 |
if (r < 0)
|
|
|
9fc0f6 |
return r;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
|
|
|
9fc0f6 |
index 5cc2c2d..376c3d4 100644
|
|
|
9fc0f6 |
--- a/src/journal/journal-file.h
|
|
|
9fc0f6 |
+++ b/src/journal/journal-file.h
|
|
|
9fc0f6 |
@@ -128,6 +128,10 @@ int journal_file_open_reliably(
|
|
|
9fc0f6 |
#define ALIGN64(x) (((x) + 7ULL) & ~7ULL)
|
|
|
9fc0f6 |
#define VALID64(x) (((x) & 7ULL) == 0ULL)
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
+/* Use six characters to cover the offsets common in smallish journal
|
|
|
9fc0f6 |
+ * files without adding too many zeros. */
|
|
|
9fc0f6 |
+#define OFSfmt "%06"PRIx64
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
static inline bool VALID_REALTIME(uint64_t u) {
|
|
|
9fc0f6 |
/* This considers timestamps until the year 3112 valid. That should be plenty room... */
|
|
|
9fc0f6 |
return u > 0 && u < (1ULL << 55);
|
|
|
9fc0f6 |
@@ -197,3 +201,23 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *
|
|
|
9fc0f6 |
int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to);
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec);
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+static unsigned type_to_context(int type) {
|
|
|
9fc0f6 |
+ /* One context for each type, plus one catch-all for the rest */
|
|
|
9fc0f6 |
+ return type > 0 && type < _OBJECT_TYPE_MAX ? type : 0;
|
|
|
9fc0f6 |
+}
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+static inline int journal_file_object_keep(JournalFile *f, Object *o, uint64_t offset) {
|
|
|
9fc0f6 |
+ unsigned context = type_to_context(o->object.type);
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ return mmap_cache_get(f->mmap, f->fd, f->prot, context, true,
|
|
|
9fc0f6 |
+ offset, o->object.size, &f->last_stat, NULL);
|
|
|
9fc0f6 |
+}
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+static inline int journal_file_object_release(JournalFile *f, Object *o, uint64_t offset) {
|
|
|
9fc0f6 |
+ unsigned context = type_to_context(o->object.type);
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ return mmap_cache_release(f->mmap, f->fd, f->prot, context,
|
|
|
9fc0f6 |
+ offset, o->object.size);
|
|
|
9fc0f6 |
+}
|
|
|
9fc0f6 |
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
|
|
|
9fc0f6 |
index 82b0f0a..f2422ff 100644
|
|
|
9fc0f6 |
--- a/src/journal/journal-verify.c
|
|
|
9fc0f6 |
+++ b/src/journal/journal-verify.c
|
|
|
9fc0f6 |
@@ -34,10 +34,6 @@
|
|
|
9fc0f6 |
#include "compress.h"
|
|
|
9fc0f6 |
#include "fsprg.h"
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
-/* Use six characters to cover the offsets common in smallish journal
|
|
|
9fc0f6 |
- * files without adding to many zeros. */
|
|
|
9fc0f6 |
-#define OFSfmt "%06"PRIx64
|
|
|
9fc0f6 |
-
|
|
|
9fc0f6 |
static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
|
|
|
9fc0f6 |
uint64_t i;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c
|
|
|
9fc0f6 |
index 03b57be..cfb26da 100644
|
|
|
9fc0f6 |
--- a/src/journal/mmap-cache.c
|
|
|
9fc0f6 |
+++ b/src/journal/mmap-cache.c
|
|
|
9fc0f6 |
@@ -38,7 +38,7 @@ typedef struct FileDescriptor FileDescriptor;
|
|
|
9fc0f6 |
struct Window {
|
|
|
9fc0f6 |
MMapCache *cache;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
- bool keep_always;
|
|
|
9fc0f6 |
+ unsigned keep_always;
|
|
|
9fc0f6 |
bool in_unused;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
int prot;
|
|
|
9fc0f6 |
@@ -182,7 +182,7 @@ static void context_detach_window(Context *c) {
|
|
|
9fc0f6 |
c->window = NULL;
|
|
|
9fc0f6 |
LIST_REMOVE(Context, by_window, w->contexts, c);
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
- if (!w->contexts && !w->keep_always) {
|
|
|
9fc0f6 |
+ if (!w->contexts && w->keep_always == 0) {
|
|
|
9fc0f6 |
/* Not used anymore? */
|
|
|
9fc0f6 |
LIST_PREPEND(Window, unused, c->cache->unused, w);
|
|
|
9fc0f6 |
if (!c->cache->last_unused)
|
|
|
9fc0f6 |
@@ -357,7 +357,6 @@ static int try_context(
|
|
|
9fc0f6 |
assert(m->n_ref > 0);
|
|
|
9fc0f6 |
assert(fd >= 0);
|
|
|
9fc0f6 |
assert(size > 0);
|
|
|
9fc0f6 |
- assert(ret);
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
c = hashmap_get(m->contexts, UINT_TO_PTR(context+1));
|
|
|
9fc0f6 |
if (!c)
|
|
|
9fc0f6 |
@@ -375,9 +374,10 @@ static int try_context(
|
|
|
9fc0f6 |
return 0;
|
|
|
9fc0f6 |
}
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
- c->window->keep_always = c->window->keep_always || keep_always;
|
|
|
9fc0f6 |
+ c->window->keep_always += keep_always;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
- *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
|
|
|
9fc0f6 |
+ if (ret)
|
|
|
9fc0f6 |
+ *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
|
|
|
9fc0f6 |
return 1;
|
|
|
9fc0f6 |
}
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
@@ -399,7 +399,6 @@ static int find_mmap(
|
|
|
9fc0f6 |
assert(m->n_ref > 0);
|
|
|
9fc0f6 |
assert(fd >= 0);
|
|
|
9fc0f6 |
assert(size > 0);
|
|
|
9fc0f6 |
- assert(ret);
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
|
|
|
9fc0f6 |
if (!f)
|
|
|
9fc0f6 |
@@ -419,9 +418,10 @@ static int find_mmap(
|
|
|
9fc0f6 |
return -ENOMEM;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
context_attach_window(c, w);
|
|
|
9fc0f6 |
- w->keep_always = w->keep_always || keep_always;
|
|
|
9fc0f6 |
+ w->keep_always += keep_always;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
- *ret = (uint8_t*) w->ptr + (offset - w->offset);
|
|
|
9fc0f6 |
+ if (ret)
|
|
|
9fc0f6 |
+ *ret = (uint8_t*) w->ptr + (offset - w->offset);
|
|
|
9fc0f6 |
return 1;
|
|
|
9fc0f6 |
}
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
@@ -447,7 +447,6 @@ static int add_mmap(
|
|
|
9fc0f6 |
assert(m->n_ref > 0);
|
|
|
9fc0f6 |
assert(fd >= 0);
|
|
|
9fc0f6 |
assert(size > 0);
|
|
|
9fc0f6 |
- assert(ret);
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
woffset = offset & ~((uint64_t) page_size() - 1ULL);
|
|
|
9fc0f6 |
wsize = size + (offset - woffset);
|
|
|
9fc0f6 |
@@ -517,7 +516,8 @@ static int add_mmap(
|
|
|
9fc0f6 |
c->window = w;
|
|
|
9fc0f6 |
LIST_PREPEND(Context, by_window, w->contexts, c);
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
- *ret = (uint8_t*) w->ptr + (offset - w->offset);
|
|
|
9fc0f6 |
+ if (ret)
|
|
|
9fc0f6 |
+ *ret = (uint8_t*) w->ptr + (offset - w->offset);
|
|
|
9fc0f6 |
return 1;
|
|
|
9fc0f6 |
}
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
@@ -538,7 +538,6 @@ int mmap_cache_get(
|
|
|
9fc0f6 |
assert(m->n_ref > 0);
|
|
|
9fc0f6 |
assert(fd >= 0);
|
|
|
9fc0f6 |
assert(size > 0);
|
|
|
9fc0f6 |
- assert(ret);
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
/* Check whether the current context is the right one already */
|
|
|
9fc0f6 |
r = try_context(m, fd, prot, context, keep_always, offset, size, ret);
|
|
|
9fc0f6 |
@@ -554,6 +553,42 @@ int mmap_cache_get(
|
|
|
9fc0f6 |
return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret);
|
|
|
9fc0f6 |
}
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
+int mmap_cache_release(
|
|
|
9fc0f6 |
+ MMapCache *m,
|
|
|
9fc0f6 |
+ int fd,
|
|
|
9fc0f6 |
+ int prot,
|
|
|
9fc0f6 |
+ unsigned context,
|
|
|
9fc0f6 |
+ uint64_t offset,
|
|
|
9fc0f6 |
+ size_t size) {
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ FileDescriptor *f;
|
|
|
9fc0f6 |
+ Window *w;
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ assert(m);
|
|
|
9fc0f6 |
+ assert(m->n_ref > 0);
|
|
|
9fc0f6 |
+ assert(fd >= 0);
|
|
|
9fc0f6 |
+ assert(size > 0);
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
|
|
|
9fc0f6 |
+ if (!f)
|
|
|
9fc0f6 |
+ return -EBADF;
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ assert(f->fd == fd);
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ LIST_FOREACH(by_fd, w, f->windows)
|
|
|
9fc0f6 |
+ if (window_matches(w, fd, prot, offset, size))
|
|
|
9fc0f6 |
+ break;
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ if (!w)
|
|
|
9fc0f6 |
+ return -ENOENT;
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ if (w->keep_always == 0)
|
|
|
9fc0f6 |
+ return -ENOLCK;
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ w->keep_always -= 1;
|
|
|
9fc0f6 |
+ return 0;
|
|
|
9fc0f6 |
+}
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
void mmap_cache_close_fd(MMapCache *m, int fd) {
|
|
|
9fc0f6 |
FileDescriptor *f;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
diff --git a/src/journal/mmap-cache.h b/src/journal/mmap-cache.h
|
|
|
9fc0f6 |
index 0c42fb8..e5e3b38 100644
|
|
|
9fc0f6 |
--- a/src/journal/mmap-cache.h
|
|
|
9fc0f6 |
+++ b/src/journal/mmap-cache.h
|
|
|
9fc0f6 |
@@ -31,6 +31,22 @@ MMapCache* mmap_cache_new(void);
|
|
|
9fc0f6 |
MMapCache* mmap_cache_ref(MMapCache *m);
|
|
|
9fc0f6 |
MMapCache* mmap_cache_unref(MMapCache *m);
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
-int mmap_cache_get(MMapCache *m, int fd, int prot, unsigned context, bool keep_always, uint64_t offset, size_t size, struct stat *st, void **ret);
|
|
|
9fc0f6 |
+int mmap_cache_get(
|
|
|
9fc0f6 |
+ MMapCache *m,
|
|
|
9fc0f6 |
+ int fd,
|
|
|
9fc0f6 |
+ int prot,
|
|
|
9fc0f6 |
+ unsigned context,
|
|
|
9fc0f6 |
+ bool keep_always,
|
|
|
9fc0f6 |
+ uint64_t offset,
|
|
|
9fc0f6 |
+ size_t size,
|
|
|
9fc0f6 |
+ struct stat *st,
|
|
|
9fc0f6 |
+ void **ret);
|
|
|
9fc0f6 |
+int mmap_cache_release(
|
|
|
9fc0f6 |
+ MMapCache *m,
|
|
|
9fc0f6 |
+ int fd,
|
|
|
9fc0f6 |
+ int prot,
|
|
|
9fc0f6 |
+ unsigned context,
|
|
|
9fc0f6 |
+ uint64_t offset,
|
|
|
9fc0f6 |
+ size_t size);
|
|
|
9fc0f6 |
void mmap_cache_close_fd(MMapCache *m, int fd);
|
|
|
9fc0f6 |
void mmap_cache_close_context(MMapCache *m, unsigned context);
|
|
|
9fc0f6 |
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
|
|
|
9fc0f6 |
index 9676f0f..67a77e6 100644
|
|
|
9fc0f6 |
--- a/src/journal/sd-journal.c
|
|
|
9fc0f6 |
+++ b/src/journal/sd-journal.c
|
|
|
9fc0f6 |
@@ -2506,9 +2506,7 @@ _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
|
|
|
9fc0f6 |
}
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
|
|
|
9fc0f6 |
- Object *o;
|
|
|
9fc0f6 |
size_t k;
|
|
|
9fc0f6 |
- int r;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
if (!j)
|
|
|
9fc0f6 |
return -EINVAL;
|
|
|
9fc0f6 |
@@ -2533,9 +2531,11 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
|
|
|
9fc0f6 |
for (;;) {
|
|
|
9fc0f6 |
JournalFile *of;
|
|
|
9fc0f6 |
Iterator i;
|
|
|
9fc0f6 |
+ Object *o;
|
|
|
9fc0f6 |
const void *odata;
|
|
|
9fc0f6 |
size_t ol;
|
|
|
9fc0f6 |
bool found;
|
|
|
9fc0f6 |
+ int r;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
/* Proceed to next data object in the field's linked list */
|
|
|
9fc0f6 |
if (j->unique_offset == 0) {
|
|
|
9fc0f6 |
@@ -2572,8 +2572,16 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
|
|
|
9fc0f6 |
return r;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
/* Let's do the type check by hand, since we used 0 context above. */
|
|
|
9fc0f6 |
- if (o->object.type != OBJECT_DATA)
|
|
|
9fc0f6 |
+ if (o->object.type != OBJECT_DATA) {
|
|
|
9fc0f6 |
+ log_error("%s:offset " OFSfmt ": object has type %d, expected %d",
|
|
|
9fc0f6 |
+ j->unique_file->path, j->unique_offset,
|
|
|
9fc0f6 |
+ o->object.type, OBJECT_DATA);
|
|
|
9fc0f6 |
return -EBADMSG;
|
|
|
9fc0f6 |
+ }
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
+ r = journal_file_object_keep(j->unique_file, o, j->unique_offset);
|
|
|
9fc0f6 |
+ if (r < 0)
|
|
|
9fc0f6 |
+ return r;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
r = return_data(j, j->unique_file, o, &odata, &ol);
|
|
|
9fc0f6 |
if (r < 0)
|
|
|
9fc0f6 |
@@ -2607,6 +2615,10 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
|
|
|
9fc0f6 |
if (found)
|
|
|
9fc0f6 |
continue;
|
|
|
9fc0f6 |
|
|
|
9fc0f6 |
+ r = journal_file_object_release(j->unique_file, o, j->unique_offset);
|
|
|
9fc0f6 |
+ if (r < 0)
|
|
|
9fc0f6 |
+ return r;
|
|
|
9fc0f6 |
+
|
|
|
9fc0f6 |
r = return_data(j, j->unique_file, o, data, l);
|
|
|
9fc0f6 |
if (r < 0)
|
|
|
9fc0f6 |
return r;
|