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

There 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 anyone who could do this could already
modify $GIT_DIR/index. A more realistic risk is some bugs in
index-helper that 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>
Signed-off-by: David Turner <dtur...@twopensource.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 f892184..1f92c89 100644
--- a/Documentation/git-index-helper.txt
+++ b/Documentation/git-index-helper.txt
@@ -25,6 +25,15 @@ OPTIONS
        Exit if the cached index is not accessed for `<n>`
        seconds. Specify 0 to wait forever. Default is 600.
 
+--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
 -----
 
diff --git a/cache.h b/cache.h
index 2d7af6f..6cb0d02 100644
--- a/cache.h
+++ b/cache.h
@@ -345,6 +345,7 @@ struct index_state {
                  * on it.
                  */
                 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 b9443f4..bc7e110 100644
--- a/index-helper.c
+++ b/index-helper.c
@@ -17,6 +17,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)
 {
@@ -114,11 +115,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);
 }
 
@@ -247,6 +293,8 @@ int main(int argc, char **argv)
        struct option options[] = {
                OPT_INTEGER(0, "exit-after", &idle_in_seconds,
                            N_("exit if not used after some seconds")),
+               OPT_BOOL(0, "strict", &to_verify,
+                        "verify shared memory after creating"),
                OPT_END()
        };
 
diff --git a/read-cache.c b/read-cache.c
index 1a5a03d..d7849d6 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1674,9 +1674,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