index: be careful when handling long names

We currently use lower 12-bit (masked with CE_NAMEMASK) in the
ce_flags field to store the length of the name in cache_entry,
without checking the length parameter given to
create_ce_flags().  This can make us store incorrect length.

Currently we are mostly protected by the fact that many
codepaths first copy the path in a variable of size PATH_MAX,
which typically is 4096 that happens to match the limit, but
that feels like a bug waiting to happen.  Besides, that would
not allow us to shorten the width of CE_NAMEMASK to use the bits
for new flags.

This redefines the meaning of the name length stored in the
cache_entry.  A name that does not fit is represented by storing
CE_NAMEMASK in the field, and the actual length needs to be
computed by actually counting the bytes in the name[] field.
This way, only the unusually long paths need to suffer.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Junio C Hamano 2008-01-18 23:42:00 -08:00 коммит произвёл Linus Torvalds
Родитель 7a51ed66f6
Коммит 7fec10b7f4
3 изменённых файлов: 46 добавлений и 3 удалений

17
cache.h
Просмотреть файл

@ -131,8 +131,21 @@ struct cache_entry {
#define CE_UPDATE (0x10000)
#define CE_REMOVE (0x20000)
#define create_ce_flags(len, stage) ((len) | ((stage) << CE_STAGESHIFT))
#define ce_namelen(ce) (CE_NAMEMASK & (ce)->ce_flags)
static inline unsigned create_ce_flags(size_t len, unsigned stage)
{
if (len >= CE_NAMEMASK)
len = CE_NAMEMASK;
return (len | (stage << CE_STAGESHIFT));
}
static inline size_t ce_namelen(const struct cache_entry *ce)
{
size_t len = ce->ce_flags & CE_NAMEMASK;
if (len < CE_NAMEMASK)
return len;
return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
}
#define ce_size(ce) cache_entry_size(ce_namelen(ce))
#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)

Просмотреть файл

@ -928,6 +928,8 @@ int read_index(struct index_state *istate)
static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
{
size_t len;
ce->ce_ctime = ntohl(ondisk->ctime.sec);
ce->ce_mtime = ntohl(ondisk->mtime.sec);
ce->ce_dev = ntohl(ondisk->dev);
@ -939,7 +941,15 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
/* On-disk flags are just 16 bits */
ce->ce_flags = ntohs(ondisk->flags);
hashcpy(ce->sha1, ondisk->sha1);
memcpy(ce->name, ondisk->name, ce_namelen(ce)+1);
len = ce->ce_flags & CE_NAMEMASK;
if (len == CE_NAMEMASK)
len = strlen(ondisk->name);
/*
* NEEDSWORK: If the original index is crafted, this copy could
* go unchecked.
*/
memcpy(ce->name, ondisk->name, len + 1);
}
/* remember to discard_cache() before reading a different cache! */

Просмотреть файл

@ -297,4 +297,24 @@ test_expect_success 'absolute path works as expected' '
test "$sym" = "$(test-absolute-path $dir2/syml)"
'
test_expect_success 'very long name in the index handled sanely' '
a=a && # 1
a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 16
a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 256
a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 4096
a=${a}q &&
>path4 &&
git update-index --add path4 &&
(
git ls-files -s path4 |
sed -e "s/ .*/ /" |
tr -d "\012"
echo "$a"
) | git update-index --index-info &&
len=$(git ls-files "a*" | wc -c) &&
test $len = 4098
'
test_done