diff options
author | Jim Meyering <meyering@redhat.com> | 2012-04-25 15:24:17 -0400 |
---|---|---|
committer | Josef Bacik <josef@redhat.com> | 2012-05-30 10:23:32 -0400 |
commit | f60d16a8923201bb27ad7c09016abab2818cf8ce (patch) | |
tree | 7bb7f019dad20883b01cb7689d80195d483e37a4 /fs/btrfs/super.c | |
parent | a27202fbe92b12eec895c36644440175de01d7a6 (diff) |
Btrfs: avoid buffer overrun in mount option handling
There is an off-by-one error: allocating room for a maximal result
string but without room for a trailing NUL. That, can lead to
returning a transformed string that is not NUL-terminated, and
then to a caller reading beyond end of the malloc'd buffer.
Rewrite to s/kzalloc/kmalloc/, remove unwarranted use of strncpy
(the result is guaranteed to fit), remove dead strlen at end, and
change a few variable names and comments.
Reviewed-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Jim Meyering <meyering@redhat.com>
Diffstat (limited to 'fs/btrfs/super.c')
-rw-r--r-- | fs/btrfs/super.c | 67 |
1 files changed, 26 insertions, 41 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 46b26650415f..96eb9fef7bd2 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -923,63 +923,48 @@ static inline int is_subvolume_inode(struct inode *inode) | |||
923 | */ | 923 | */ |
924 | static char *setup_root_args(char *args) | 924 | static char *setup_root_args(char *args) |
925 | { | 925 | { |
926 | unsigned copied = 0; | 926 | unsigned len = strlen(args) + 2 + 1; |
927 | unsigned len = strlen(args) + 2; | 927 | char *src, *dst, *buf; |
928 | char *pos; | ||
929 | char *ret; | ||
930 | 928 | ||
931 | /* | 929 | /* |
932 | * We need the same args as before, but minus | 930 | * We need the same args as before, but with this substitution: |
931 | * s!subvol=[^,]+!subvolid=0! | ||
933 | * | 932 | * |
934 | * subvol=a | 933 | * Since the replacement string is up to 2 bytes longer than the |
935 | * | 934 | * original, allocate strlen(args) + 2 + 1 bytes. |
936 | * and add | ||
937 | * | ||
938 | * subvolid=0 | ||
939 | * | ||
940 | * which is a difference of 2 characters, so we allocate strlen(args) + | ||
941 | * 2 characters. | ||
942 | */ | 935 | */ |
943 | ret = kzalloc(len * sizeof(char), GFP_NOFS); | ||
944 | if (!ret) | ||
945 | return NULL; | ||
946 | pos = strstr(args, "subvol="); | ||
947 | 936 | ||
937 | src = strstr(args, "subvol="); | ||
948 | /* This shouldn't happen, but just in case.. */ | 938 | /* This shouldn't happen, but just in case.. */ |
949 | if (!pos) { | 939 | if (!src) |
950 | kfree(ret); | 940 | return NULL; |
941 | |||
942 | buf = dst = kmalloc(len, GFP_NOFS); | ||
943 | if (!buf) | ||
951 | return NULL; | 944 | return NULL; |
952 | } | ||
953 | 945 | ||
954 | /* | 946 | /* |
955 | * The subvol=<> arg is not at the front of the string, copy everybody | 947 | * If the subvol= arg is not at the start of the string, |
956 | * up to that into ret. | 948 | * copy whatever precedes it into buf. |
957 | */ | 949 | */ |
958 | if (pos != args) { | 950 | if (src != args) { |
959 | *pos = '\0'; | 951 | *src++ = '\0'; |
960 | strcpy(ret, args); | 952 | strcpy(buf, args); |
961 | copied += strlen(args); | 953 | dst += strlen(args); |
962 | pos++; | ||
963 | } | 954 | } |
964 | 955 | ||
965 | strncpy(ret + copied, "subvolid=0", len - copied); | 956 | strcpy(dst, "subvolid=0"); |
966 | 957 | dst += strlen("subvolid=0"); | |
967 | /* Length of subvolid=0 */ | ||
968 | copied += 10; | ||
969 | 958 | ||
970 | /* | 959 | /* |
971 | * If there is no , after the subvol= option then we know there's no | 960 | * If there is a "," after the original subvol=... string, |
972 | * other options and we can just return. | 961 | * copy that suffix into our buffer. Otherwise, we're done. |
973 | */ | 962 | */ |
974 | pos = strchr(pos, ','); | 963 | src = strchr(src, ','); |
975 | if (!pos) | 964 | if (src) |
976 | return ret; | 965 | strcpy(dst, src); |
977 | 966 | ||
978 | /* Copy the rest of the arguments into our buffer */ | 967 | return buf; |
979 | strncpy(ret + copied, pos, len - copied); | ||
980 | copied += strlen(pos); | ||
981 | |||
982 | return ret; | ||
983 | } | 968 | } |
984 | 969 | ||
985 | static struct dentry *mount_subvol(const char *subvol_name, int flags, | 970 | static struct dentry *mount_subvol(const char *subvol_name, int flags, |