From: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>

There's are "holes" in the index-helper approach because the shared
memory is not verified again by git. If $USER is compromised, shared
memory could be modified. But then they can already modify
$GIT_DIR/index. A more realistic risk is some bugs in index-helper
produce corrupt shared memory. --strict is added to avoid that

Strictly speaking there's still a very small gap where corrupt shared
memory could still be read by git: after we write the trailing SHA-1 in
the shared memory (thus signaling "this shm is ready") and before
verify_shm() detects an error.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>
---
 Documentation/git-index-helper.txt |  9 +++++++
 cache.h                            |  1 +
 index-helper.c                     | 48 ++++++++++++++++++++++++++++++++++++++
 read-cache.c                       |  9 ++++---
 4 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-index-helper.txt 
b/Documentation/git-index-helper.txt
index 9db28cf..ad40366 100644
--- a/Documentation/git-index-helper.txt
+++ b/Documentation/git-index-helper.txt
@@ -22,6 +22,15 @@ OPTIONS
        Exit if the cached index is not accessed for `<n>`
        minutes. Specify 0 to wait forever. Default is 10.
 
+--strict::
+--no-strict::
+       Strict mode makes index-helper verify the shared memory after
+       it's created. If the result does not match what's read from
+       $GIT_DIR/index, the shared memory is destroyed. This makes
+       index-helper take more than double the amount of time required
+       for reading an index, but because it will happen in the
+       background, it's not noticable. `--strict` is enabled by default.
+
 NOTES
 -----
 On UNIX-like systems, $GIT_DIR/index-helper.pid contains the process
diff --git a/cache.h b/cache.h
index e22296c..1d7e561 100644
--- a/cache.h
+++ b/cache.h
@@ -336,6 +336,7 @@ struct index_state {
                 keep_mmap : 1,
                 from_shm : 1,
                 to_shm : 1,
+                always_verify_trailing_sha1 : 1,
                 initialized : 1;
        struct hashmap name_hash;
        struct hashmap dir_hash;
diff --git a/index-helper.c b/index-helper.c
index cf2971d..1140bc0 100644
--- a/index-helper.c
+++ b/index-helper.c
@@ -14,6 +14,7 @@ struct shm {
 
 static struct shm shm_index;
 static struct shm shm_base_index;
+static int to_verify = 1;
 
 static void release_index_shm(struct shm *is)
 {
@@ -69,11 +70,56 @@ static void share_index(struct index_state *istate, struct 
shm *is)
        hashcpy((unsigned char *)new_mmap + istate->mmap_size - 20, is->sha1);
 }
 
+static int verify_shm(void)
+{
+       int i;
+       struct index_state istate;
+       memset(&istate, 0, sizeof(istate));
+       istate.always_verify_trailing_sha1 = 1;
+       istate.to_shm = 1;
+       i = read_index(&istate);
+       if (i != the_index.cache_nr)
+               goto done;
+       for (; i < the_index.cache_nr; i++) {
+               struct cache_entry *base, *ce;
+               /* namelen is checked separately */
+               const unsigned int ondisk_flags =
+                       CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS;
+               unsigned int ce_flags, base_flags, ret;
+               base = the_index.cache[i];
+               ce = istate.cache[i];
+               if (ce->ce_namelen != base->ce_namelen ||
+                   strcmp(ce->name, base->name)) {
+                       warning("mismatch at entry %d", i);
+                       break;
+               }
+               ce_flags = ce->ce_flags;
+               base_flags = base->ce_flags;
+               /* only on-disk flags matter */
+               ce->ce_flags   &= ondisk_flags;
+               base->ce_flags &= ondisk_flags;
+               ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data,
+                            offsetof(struct cache_entry, name) -
+                            offsetof(struct cache_entry, ce_stat_data));
+               ce->ce_flags = ce_flags;
+               base->ce_flags = base_flags;
+               if (ret) {
+                       warning("mismatch at entry %d", i);
+                       break;
+               }
+       }
+done:
+       discard_index(&istate);
+       return i == the_index.cache_nr;
+}
+
 static void share_the_index(void)
 {
        if (the_index.split_index && the_index.split_index->base)
                share_index(the_index.split_index->base, &shm_base_index);
        share_index(&the_index, &shm_index);
+       if (to_verify && !verify_shm())
+               cleanup_shm();
        discard_index(&the_index);
 }
 
@@ -130,6 +176,8 @@ int main(int argc, char **argv)
        struct option options[] = {
                OPT_INTEGER(0, "exit-after", &idle_in_minutes,
                            N_("exit if not used after some minutes")),
+               OPT_BOOL(0, "strict", &to_verify,
+                        "verify shared memory after creating"),
                OPT_END()
        };
 
diff --git a/read-cache.c b/read-cache.c
index 7bd3ce4..1a0ab0c 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1660,9 +1660,12 @@ int do_read_index(struct index_state *istate, const char 
*path, int must_exist)
 
        istate->mmap = mmap;
        istate->mmap_size = mmap_size;
-       if (try_shm(istate) &&
-           verify_hdr(istate->mmap, istate->mmap_size) < 0)
-               goto unmap;
+       if (try_shm(istate)) {
+               if (verify_hdr(istate->mmap, istate->mmap_size) < 0)
+                       goto unmap;
+       } else if (istate->always_verify_trailing_sha1 &&
+                  verify_hdr(istate->mmap, istate->mmap_size) < 0)
+                       goto unmap;
        hdr = mmap = istate->mmap;
        mmap_size = istate->mmap_size;
        if (!istate->keep_mmap)
-- 
2.4.2.767.g62658d5-twtrsrc

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to