Revert "pcdp: use early_ioremap/early_iounmap to
Subsystems (89)
PATCH 133/184 udf: fix memory leak while allocating blocks during
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
write
From: Namjae Jeon <[email protected]>
commit 2fb7d99d0de3fd8ae869f35ab682581d8455887a upstream.
Need to brelse the buffer_head stored in cur_epos and next_epos.
--- fs/udf/inode.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/udf/inode.c +4 -0
@@ -648,6 +648,8 @@ static struct buffer_head *inode_getblk(struct inode *inode, sector_t block, goal, err);if (!newblocknum) {brelse (prev_epos.bh); +brelse (cur_epos.bh); +brelse (next_epos.bh); *err = -ENOSPC ;return NULL ; }
@@ -678,6 +680,8 @@ static struct buffer_head *inode_getblk(struct inode *inode, sector_t block,udf_update_extents (inode, laarr, startnum, endnum, &prev_epos);brelse (prev_epos.bh); +brelse (cur_epos.bh); +brelse (next_epos.bh); newblock =udf_get_pblock (inode->i_sb, newblocknum, iinfo->i_location.partitionReferenceNum,0 );
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 134/184 udf: avoid info leak on export
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
commit 0143fc5e9f6f5aad4764801015bc8d4b4a278200 upstream.
For type 0x51 the udf.parent_partref member in struct fid gets copied uninitialized to userland. Fix this by initializing it to 0.
--- fs/udf/namei.c | 1 + 1 file changed, 1 insertion(+)
fs/udf/namei.c +1 -0
@@ -1331,6 +1331,7 @@ static int udf_encode_fh(struct dentry *de, __u32 *fh, int *lenp, *lenp =3 ; fid->udf.block = location.logicalBlockNum; fid->udf.partref = location.partitionReferenceNum; + fid->udf.parent_partref =0 ; fid->udf.generation = inode->i_generation;if (connectable && !S_ISDIR (inode->i_mode)) {
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 135/184 udf: Fix bitmap overflow on large filesystems with
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
small block size
From: Jan Kara <[email protected]>
commit 89b1f39eb4189de745fae554b0d614d87c8d5c63 upstream.
For large UDF filesystems with 512-byte blocks the number of necessary bitmap blocks is larger than 2^16 so s_nr_groups in udf_bitmap overflows (the number will overflow for filesystems larger than 128 GB with 512-byte blocks). That results in ENOSPC errors despite the filesystem has plenty of free space.
Fix the problem by changing s_nr_groups' type to 'int'. That is enough even for filesystems 2^32 blocks (UDF maximum) and 512-byte blocksize.
--- fs/udf/udf_sb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/udf/udf_sb.h +1 -1
@@ -78,7 +78,7 @@ struct udf_virtual_data {struct udf_bitmap {__u32 s_extLength;__u32 s_extPosition; -__u16 s_nr_groups; +int s_nr_groups;struct buffer_head **s_block_bitmap; };
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 136/184 fs/cifs/cifs_dfs_ref.c: fix potential memory leakage
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Cong Ding <[email protected]>
commit 10b8c7dff5d3633b69e77f57d404dab54ead3787 upstream.
When it goes to error through line 144, the memory allocated to *devname is not freed, and the caller doesn't free it either in line 250. So we free the memroy of *devname in function cifs_compose_mount_options() when it goes to error.
--- fs/cifs/cifs_dfs_ref.c | 2 ++ 1 file changed, 2 insertions(+)
fs/cifs/cifs_dfs_ref.c +2 -0
@@ -226,6 +226,8 @@ compose_mount_options_out: compose_mount_options_err:kfree (mountdata); mountdata =ERR_PTR (rc); +kfree (*devname); + *devname =NULL ; goto compose_mount_options_out; }
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 137/184 isofs: avoid info leak on export
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
commit fe685aabf7c8c9f138e5ea900954d295bf229175 upstream.
For type 1 the parent_offset member in struct isofs_fid gets copied uninitialized to userland. Fix this by initializing it to 0.
--- fs/isofs/export.c | 1 + 1 file changed, 1 insertion(+)
fs/isofs/export.c +1 -0
@@ -131,6 +131,7 @@ isofs_export_encode_fh(struct dentry *dentry, len =3 ; fh32[0 ] = ei->i_iget5_block; fh16[2 ] = (__u16 )ei->i_iget5_offset;/* fh16 [sic] */ + fh16[3 ] =0 ;/* avoid leaking uninitialized data */ fh32[2 ] = inode->i_generation;if (connectable && !S_ISDIR (inode->i_mode)) {struct inode *parent;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 138/184 fat: Fix stat->f_namelen
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Dankwardt <[email protected]>
commit eeb5b4ae81f4a750355fa0c15f4fea22fdf83be1 upstream.
I found that the length of a file name when created cannot exceed 255 characters, yet, pathconf(), via statfs(), returns the maximum as 260.
--- fs/fat/inode.c | 2 +- fs/fat/namei_vfat.c | 6 +++--- include/linux/msdos_fs.h | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-)
fs/fat/inode.c +1 -1
@@ -558,7 +558,7 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = sbi->free_clusters; buf->f_fsid.val[0 ] = (u32 )id; buf->f_fsid.val[1 ] = (u32)(id >>32 ); - buf->f_namelen = sbi->options.isvfat ?260 :12 ; + buf->f_namelen = sbi->options.isvfat ?FAT_LFN_LEN :12 ;return 0 ; }
fs/fat/namei_vfat.c +3 -3
@@ -502,14 +502,14 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, *outlen =utf8s_to_utf16s (name, len, (wchar_t *)outname);if (*outlen <0 )return *outlen; -else if (*outlen >255 ) +else if (*outlen >FAT_LFN_LEN )return -ENAMETOOLONG ; op = &outname[*outlen *sizeof (wchar_t)]; }else {if (nls) {for (i =0 , ip = name, op = outname, *outlen =0 ; - i < len && *outlen <=255 ; + i < len && *outlen <=FAT_LFN_LEN ; *outlen +=1 ) {if (escape && (*ip ==':' )) {
@@ -549,7 +549,7 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,return -ENAMETOOLONG ; }else {for (i =0 , ip = name, op = outname, *outlen =0 ; - i < len && *outlen <=255 ; + i < len && *outlen <=FAT_LFN_LEN ; i++, *outlen +=1 ) { *op++ = *ip++;
include/linux/msdos_fs.h +2 -1
@@ -15,6 +15,7 @@#define MSDOS_DPB_BITS 4/* log2(MSDOS_DPB) */ #define MSDOS_DPS (SECTOR_SIZE / sizeof(struct msdos_dir_entry))#define MSDOS_DPS_BITS 4/* log2(MSDOS_DPS) */ +#define MSDOS_LONGNAME 256/* maximum name length */ #define CF_LE_W (v) le16_to_cpu(v)#define CF_LE_L (v) le32_to_cpu(v)#define CT_LE_W (v) cpu_to_le16(v)
@@ -47,8 +48,8 @@#define DELETED_FLAG 0xe5/* marks file as deleted when in name[0] */ #define IS_FREE (n) (!*(n) || *(n) == DELETED_FLAG) +#define FAT_LFN_LEN 255/* maximum long name length */ #define MSDOS_NAME 11/* maximum name length */ -#define MSDOS_LONGNAME 256/* maximum name length */ #define MSDOS_SLOTS 21/* max # of slots for short and long names */ #define MSDOS_DOT ". "/* ".", padded to MSDOS_NAME chars */ #define MSDOS_DOTDOT ".. "/* "..", padded to MSDOS_NAME chars */
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 139/184 NLS: improve UTF8 -> UTF16 string conversion routine
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Alan Stern <[email protected]>
commit 0720a06a7518c9d0c0125bd5d1f3b6264c55c3dd upstream.
The utf8s_to_utf16s conversion routine needs to be improved. Unlike its utf16s_to_utf8s sibling, it doesn't accept arguments specifying the maximum length of the output buffer or the endianness of its 16-bit output.
This patch (as1501) adds the two missing arguments, and adjusts the only two places in the kernel where the function is called. A follow-on patch will add a third caller that does utilize the new capabilities.
The two conversion routines are still annoyingly inconsistent in the way they handle invalid byte combinations. But that's a subject for a different patch.
--- fs/fat/namei_vfat.c | 3 ++- fs/nls/nls_base.c | 43 +++++++++++++++++++++++++++++++++---------- include/linux/nls.h | 5 +++-- 3 files changed, 38 insertions(+), 13 deletions(-)
fs/fat/namei_vfat.c +2 -1
@@ -499,7 +499,8 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,int charlen;if (utf8) { - *outlen =utf8s_to_utf16s (name, len, (wchar_t *)outname); + *outlen =utf8s_to_utf16s (name, len,UTF16_HOST_ENDIAN , + (wchar_t *) outname,FAT_LFN_LEN +2 );if (*outlen <0 )return *outlen;else if (*outlen >FAT_LFN_LEN )
fs/nls/nls_base.c +33 -10
67 lines not on this page · raw message
include/linux/nls.h +3 -2
@@ -43,7 +43,7 @@ enum utf16_endian { UTF16_BIG_ENDIAN }; -/* nls.c */ +/* nls_base.c */ extern int register_nls (struct nls_table *);extern int unregister_nls (struct nls_table *);extern struct nls_table *load_nls (char *);
@@ -52,7 +52,8 @@ extern struct nls_table *load_nls_default(void);extern int utf8_to_utf32 (const u8 *s,int len,unicode_t *pu);extern int utf32_to_utf8 (unicode_t u,u8 *s,int maxlen); -extern int utf8s_to_utf16s (const u8 *s,int len,wchar_t *pwcs); +extern int utf8s_to_utf16s (const u8 *s,int len, +enum utf16_endian endian,wchar_t *pwcs,int maxlen);extern int utf16s_to_utf8s (const wchar_t *pwcs,int len,enum utf16_endian endian,u8 *s,int maxlen);
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
Re: [ 139/184] NLS: improve UTF8 -> UTF16 string conversion routine
Quoted 18 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Alan Stern <[email protected]>
commit
0720a06a7518c9d0c0125bd5d1f3b6264c55c3ddupstream.The
utf8s_to_utf16sconversion routine needs to be improved. Unlike itsutf16s_to_utf8ssibling, it doesn't accept arguments specifying the maximum length of the output buffer or the endianness of its 16-bit output.This patch (as1501) adds the two missing arguments, and adjusts the only two places in the kernel where the function is called. A follow-on patch will add a third caller that does utilize the new capabilities.
Quoted 8 lines
The two conversion routines are still annoyingly inconsistent in the way they handle invalid byte combinations. But that's a subject for a different patch.
Signed-off-by Alan Stern <[email protected]>CC Clemens Ladisch <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>[bwh: Bakckported to 2.6.32: drop Hyper-V change]
Quoted 116 lines
Signed-off-by Willy Tarreau <[email protected]>--- fs/fat/namei_vfat.c | 3 ++- fs/nls/nls_base.c | 43 +++++++++++++++++++++++++++++++++---------- include/linux/nls.h | 5 +++-- 3 files changed, 38 insertions(+), 13 deletions(-)fs/fat/namei_vfat.c +2
-1@@ -499,7 +499,8 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,int charlen;if (utf8) { - *outlen =utf8s_to_utf16s (name, len, (wchar_t *)outname); + *outlen =utf8s_to_utf16s (name, len,UTF16_HOST_ENDIAN , + (wchar_t *) outname,FAT_LFN_LEN +2 );if (*outlen <0 )return *outlen;else if (*outlen >FAT_LFN_LEN )fs/nls/nls_base.c +33
-10@@ -114,34 +114,57 @@ int utf32_to_utf8(unicode_t u, u8 *s, int maxlen) }EXPORT_SYMBOL (utf32_to_utf8); -int utf8s_to_utf16s (const u8 *s,int len,wchar_t *pwcs) +static inline void put_utf16 (wchar_t *s,unsigned c,enum utf16_endian endian) +{ +switch (endian) { +default : + *s = (wchar_t ) c; +break ; +case UTF16_LITTLE_ENDIAN : + *s =__cpu_to_le16 (c); +break ; +case UTF16_BIG_ENDIAN : + *s =__cpu_to_be16 (c); +break ; + } +} + +int utf8s_to_utf16s (const u8 *s,int len,enum utf16_endian endian, +wchar_t *pwcs,int maxlen) {u16 *op;int size;unicode_t u; op = pwcs; -while (*s && len >0 ) { +while (len >0 && maxlen >0 && *s) {if (*s &0x80 ) { size =utf8_to_utf32 (s, len, &u);if (size <0 )return -EINVAL ; + s += size; + len -= size;if (u >=PLANE_SIZE ) { +if (maxlen <2 ) +break ; u -=PLANE_SIZE ; - *op++ = (wchar_t) (SURROGATE_PAIR | - ((u >>10 ) &SURROGATE_BITS )); - *op++ = (wchar_t) (SURROGATE_PAIR | +put_utf16 (op++,SURROGATE_PAIR | + ((u >>10 ) &SURROGATE_BITS ), + endian); +put_utf16 (op++,SURROGATE_PAIR |SURROGATE_LOW | - (u &SURROGATE_BITS )); + (u &SURROGATE_BITS ), + endian); + maxlen -=2 ; }else { - *op++ = (wchar_t ) u; +put_utf16 (op++, u, endian); + maxlen--; } - s += size; - len -= size; }else { - *op++ = *s++; +put_utf16 (op++, *s++, endian); len--; + maxlen--; } }return op - pwcs;include/linux/nls.h +3
-2@@ -43,7 +43,7 @@ enum utf16_endian { UTF16_BIG_ENDIAN }; -/* nls.c */ +/* nls_base.c */ extern int register_nls (struct nls_table *);extern int unregister_nls (struct nls_table *);extern struct nls_table *load_nls (char *);@@ -52,7 +52,8 @@ extern struct nls_table *load_nls_default(void);extern int utf8_to_utf32 (const u8 *s,int len,unicode_t *pu);extern int utf32_to_utf8 (unicode_t u,u8 *s,int maxlen); -extern int utf8s_to_utf16s (const u8 *s,int len,wchar_t *pwcs); +extern int utf8s_to_utf16s (const u8 *s,int len, +enum utf16_endian endian,wchar_t *pwcs,int maxlen);extern int utf16s_to_utf8s (const wchar_t *pwcs,int len,enum utf16_endian endian,u8 *s,int maxlen);
Signature · 3 lines
Ben Hutchings
Theory and practice are closer in theory than in practice.
- John Levine, moderator of comp.compilersRe: [ 139/184] NLS: improve UTF8 -> UTF16 string conversion routine
Quoted 6 lines · Ben Hutchings
Quoted 4 lines
Signed-off-by Alan Stern <[email protected]>CC Clemens Ladisch <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>[bwh: Bakckported to 2.6.32: drop Hyper-V change]Signed-off-by Ben Hutchings <[email protected]>
added, thank you.
Willy
PATCH 140/184 hfsplus: fix potential overflow in
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
hfsplus_file_truncate()
From: Vyacheslav Dubeyko <[email protected]>
commit 12f267a20aecf8b84a2a9069b9011f1661c779b4 upstream.
Change a u32 to loff_t hfsplus_file_truncate().
--- fs/hfsplus/extents.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/hfsplus/extents.c +1 -1
@@ -447,7 +447,7 @@ void hfsplus_file_truncate(struct inode *inode)struct address_space *mapping = inode->i_mapping;struct page *page;void *fsdata; -u32 size = inode->i_size; +loff_t size = inode->i_size;int res; res = pagecache_write_begin(NULL, mapping, size, 0,
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 141/184 btrfs: use rcu_barrier() to wait for bdev puts at
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
unmount
From: Eric Sandeen <[email protected]>
commit bc178622d40d87e75abc131007342429c9b03351 upstream.
Doing this would reliably fail with -EBUSY for me:
# mount /dev/sdb2 /mnt/scratch; umount /mnt/scratch; mkfs.btrfs -f /dev/sdb2 ... unable to open /dev/sdb2: Device or resource busy
because mkfs.btrfs tries to open the device O_EXCL, and somebody still has it.
Using systemtap to track bdev gets & puts shows a kworker thread doing a blkdev put after mkfs attempts a get; this is left over from the unmount path:
btrfs_close_devices
__btrfs_close_devices call_rcu(&device->rcu, free_device); free_device INIT_WORK(&device->rcu_work, __free_device); schedule_work(&device->rcu_work);
so unmount might complete before __free_device fires & does its blkdev_put.
Adding an rcu_barrier() to btrfs_close_devices() causes unmount to wait until all blkdev_put()s are done, and the device is truly free once unmount completes.
--- fs/btrfs/volumes.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/btrfs/volumes.c +6 -0
@@ -557,6 +557,12 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)__btrfs_close_devices (fs_devices);free_fs_devices (fs_devices); } +/* +* Wait for rcu kworkers under __btrfs_close_devices +* to finish all blkdev_puts so device is really +* free when umount is done. +*/ +rcu_barrier ();return ret; }
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 142/184 kernel panic when mount NFSv4
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Trond Myklebust <[email protected]>
Quoted 74 lines · Mi Jinlong
Hi,
When testing NFSv4 at RHEL6 with kernel 2.6.32, I got a kernel panic at NFS client's
__rpc_create_commonfunction.The panic place is:
rpc_mkpipe__rpc_lookup_create() <=== find pipefile *idmap* __rpc_mkpipe() <=== pipefile is *idmap* __rpc_create_common() ****** BUG_ON(!d_unhashed(dentry)); ****** *panic*It means that the dentry's
d_flagshave be setDCACHE_UNHASHED, but it should not be set here.Is someone known this bug? or give me some idea?
A reproduce program is append, but it can't reproduce the bug every time.
the export is: "/nfsroot *(rw,no_root_squash,fsid=0,insecure)"And the panic message is append.
============================================================================
#!/bin/shLOOPTOTAL=768
LOOPCOUNT=0
ret=0while [ $LOOPCOUNT -ne $LOOPTOTAL ]
do((LOOPCOUNT += 1)) service nfs restart /usr/sbin/rpc.idmapd mount -t nfs4 127.0.0.1:/ /mnt|| return 1; ls -l /var/lib/nfs/rpc_pipefs/nfs/*/ umount /mnt echo $LOOPCOUNTdone
===============================================================================
Code: af 60 01 00 00 89 fa 89 f0 e8 64 cf 89 f0 e8 5c 7c 64 cf 31 c0 8b 5c 24 10 8b 74 24 14 8b 7c 24 18 8b 6c 24 1c 83 c4 20 c3 <0f> 0b eb fc 8b 46 28 c7 44 24 08 20 de ee f0 c7 44 24 04 56 ea EIP:[<f0ee92ea>] __rpc_create_common+0x8a/0xc0 [sunrpc] SS:ESP 0068:eccb5d28 ---[ end trace 8f5606cd08928ed2]--- Kernel panic - not syncing: Fatal exception Pid:7131, comm: mount.nfs4 Tainted: G D -------------------2.6.32 #1 Call Trace: [<c080ad18>] ? panic+0x42/0xed [<c080e42c>] ? oops_end+0xbc/0xd0 [<c040b090>] ? do_invalid_op+0x0/0x90 [<c040b10f>] ? do_invalid_op+0x7f/0x90 [<f0ee92ea>] ? __rpc_create_common+0x8a/0xc0[sunrpc] [<f0edc433>] ? rpc_free_task+0x33/0x70[sunrpc] [<f0ed6508>] ? prc_call_sync+0x48/0x60[sunrpc] [<f0ed656e>] ? rpc_ping+0x4e/0x60[sunrpc] [<f0ed6eaf>] ? rpc_create+0x38f/0x4f0[sunrpc] [<c080d80b>] ? error_code+0x73/0x78 [<f0ee92ea>] ? __rpc_create_common+0x8a/0xc0[sunrpc] [<c0532bda>] ? d_lookup+0x2a/0x40 [<f0ee94b1>] ? rpc_mkpipe+0x111/0x1b0[sunrpc] [<f10a59f4>] ? nfs_create_rpc_client+0xb4/0xf0[nfs] [<f10d6c6d>] ? nfs_fscache_get_client_cookie+0x1d/0x50[nfs] [<f10d3fcb>] ? nfs_idmap_new+0x7b/0x140[nfs] [<c05e76aa>] ? strlcpy+0x3a/0x60 [<f10a60ca>] ? nfs4_set_client+0xea/0x2b0[nfs] [<f10a6d0c>] ? nfs4_create_server+0xac/0x1b0[nfs] [<c04f1400>] ? krealloc+0x40/0x50 [<f10b0e8b>] ? nfs4_remote_get_sb+0x6b/0x250[nfs] [<c04f14ec>] ? kstrdup+0x3c/0x60 [<c0520739>] ? vfs_kern_mount+0x69/0x170 [<f10b1a3c>] ? nfs_do_root_mount+0x6c/0xa0[nfs] [<f10b1b47>] ? nfs4_try_mount+0x37/0xa0[nfs]
Quoted 8 lines
[<f10afe6d>] ? nfs4_validate_text_mount_data+-x7d/0xf0[nfs] [<f10b1c42>] ? nfs4_get_sb+0x92/0x2f0 [<c0520739>] ? vfs_kern_mount+0x69/0x170 [<c05366d2>] ? get_fs_type+0x32/0xb0 [<c052089f>] ? do_kern_mount+0x3f/0xe0 [<c053954f>] ? do_mount+0x2ef/0x740 [<c0537740>] ? copy_mount_options+0xb0/0x120 [<c0539a0e>] ? sys_mount+0x6e/0xa0
Hi,
Does the following patch fix the problem?
Cheers
Trond
--------------------------
SUNRPC: Fix a BUG in __rpc_create_common
From: Trond Myklebust <[email protected]>
Mi Jinlong reports:
When testing NFSv4 at RHEL6 with kernel 2.6.32, I got a kernel panic at NFS client's __rpc_create_common function.
The panic place is:
rpc_mkpipe
__rpc_lookup_create() <=== find pipefile *idmap*
__rpc_mkpipe() <=== pipefile is *idmap*
__rpc_create_common()
****** BUG_ON(!d_unhashed(dentry)); ****** *panic*The test is wrong: we can find ourselves with a hashed negative dentry here if the idmapper tried to look up the file before we got round to creating it.
Just replace the BUG_ON() with a d_drop(dentry).
[2.6.32 background info from Jonathan below]
Quoted 18 lines
Hi Willy et al,
Please consider
beb0f0a9fba1kernel panic when mount NFSv4, 2010-12-20for application to kernel.org's 2.6.32.y and 2.6.34.y trees. The patch was applied upstream during the 2.6.38 merge window, so newer kernels don't need it.
(Context: <http://bugs.debian.org/695872>.) Tom Downes (cc-ed) experienced the bug on a Debian kernel close to 2.6.32.58 and confirmed that the patch doesn't seem to hurt.
The patch is part of Fedora 13's 2.6.34-based and Fedora 14's 2.6.35-based kernels[1]. It was also included in the RHEL kernel at some point between 2.6.32-71.29.1.el6 and 2.6.32-131.0.15.el6[2].
Quoted 7 lines
Thoughts of all kinds welcome, as always.
Regards,
Jonathan[1] https://bugzilla.redhat.com/673207
[2] https://oss.oracle.com/git/?p=redpatch.git;a=commit;h=8028cccdc4b1
--- net/sunrpc/rpc_pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sunrpc/rpc_pipe.c +1 -1
@@ -459,7 +459,7 @@ static int __rpc_create_common(struct inode *dir, struct dentry *dentry, {struct inode *inode; -BUG_ON (!d_unhashed (dentry)); +d_drop (dentry); inode =rpc_get_inode (dir->i_sb, mode);if (!inode) goto out_err;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 143/184 nfsd4: fix oops on unusual readlike compound
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: "J. Bruce Fields" <[email protected]>
commit d5f50b0c290431c65377c4afa1c764e2c3fe5305 upstream.
If the argument and reply together exceed the maximum payload size, then a reply with a read-like operation can overlow the rq_pages array.
--- fs/nfsd/nfs4xdr.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
fs/nfsd/nfs4xdr.c +10 -1
@@ -2610,11 +2610,16 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, len = maxcount; v =0 ;while (len >0 ) { - pn = resp->rqstp->rq_resused++; + pn = resp->rqstp->rq_resused; +if (!resp->rqstp->rq_respages[pn]) {/* ran out of pages */ + maxcount -= len; +break ; + } resp->rqstp->rq_vec[v].iov_base =page_address (resp->rqstp->rq_respages[pn]); resp->rqstp->rq_vec[v].iov_len = len <PAGE_SIZE ? len :PAGE_SIZE ; + resp->rqstp->rq_resused++; v++; len -=PAGE_SIZE ; }
@@ -2662,6 +2667,8 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsdreturn nfserr;if (resp->xbuf->page_len)return nfserr_resource; +if (!resp->rqstp->rq_respages[resp->rqstp->rq_resused]) +return nfserr_resource; page =page_address (resp->rqstp->rq_respages[resp->rqstp->rq_resused++]);
@@ -2711,6 +2718,8 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4return nfserr;if (resp->xbuf->page_len)return nfserr_resource; +if (!resp->rqstp->rq_respages[resp->rqstp->rq_resused]) +return nfserr_resource;RESERVE_SPACE (8 );/* verifier */ savep = p;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 144/184 net/core: Fix potential memory leak in
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
dev_set_alias()
From: Alexey Khoroshilov <[email protected]>
[ Upstream commit 7364e445f62825758fa61195d237a5b8ecdd06ec ]
Do not leak memory by updating pointer with potentially NULL realloc return value.
Found by Linux Driver Verification project (linuxtesting.org).
--- net/core/dev.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/core/dev.c +5 -2
@@ -967,6 +967,8 @@ rollback:*/ int dev_set_alias (struct net_device *dev,const char *alias,size_t len) { +char *new_ifalias; +ASSERT_RTNL ();if (len >=IFALIASZ )
@@ -980,9 +982,10 @@ int dev_set_alias(struct net_device *dev, const char *alias, size_t len)return 0 ; } - dev->ifalias =krealloc (dev->ifalias, len +1 ,GFP_KERNEL ); -if (!dev->ifalias) + new_ifalias =krealloc (dev->ifalias, len +1 ,GFP_KERNEL ); +if (!new_ifalias)return -ENOMEM ; + dev->ifalias = new_ifalias;strlcpy (dev->ifalias, alias, len+1 );return len;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 145/184 net: reduce net_rx_action() latency to 2 HZ
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
We should use time_after_eq() to get maximum latency of two ticks, instead of three.
Bug added in commit 24f8b2385 (net: increase receive packet quantum)
--- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/dev.c +1 -1
@@ -2848,7 +2848,7 @@ static void net_rx_action(struct softirq_action *h)* Allow this to run for 2 jiffies since which will allow * an average latency of 1.5/HZ. */ -if (unlikely (budget <=0 ||time_after (jiffies, time_limit))) +if (unlikely (budget <=0 ||time_after_eq (jiffies, time_limit))) goto softnet_break;local_irq_enable ();
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 146/184 softirq: reduce latencies
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
In various network workloads, __do_softirq() latencies can be up to 20 ms if HZ=1000, and 200 ms if HZ=100.
This is because we iterate 10 times in the softirq dispatcher, and some actions can consume a lot of cycles.
This patch changes the fallback to ksoftirqd condition to :
- A time limit of 2 ms.
- need_resched() being set on current task
When one of this condition is met, we wakeup ksoftirqd for further softirq processing if we still have pending softirqs.
Using need_resched() as the only condition can trigger RCU stalls, as we can keep BH disabled for too long.
I ran several benchmarks and got no significant difference in throughput, but a very significant reduction of latencies (one order of magnitude) :
In following bench, 200 antagonist "netperf -t TCP_RR" are started in background, using all available cpus.
Then we start one "netperf -t TCP_RR", bound to the cpu handling the NIC IRQ (hard+soft)
Before patch :
RT_LATENCY,MIN_LATENCY,MAX_LATENCY,P50_LATENCY,P90_LATENCY,P99_LATENCY,MEAN_LATENCY,STDDEV_LATENCY MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET
to 7.7.7.84 () port 0 AF_INET : first burst 0 : cpu bind
RT_LATENCY=550110.424
MIN_LATENCY=146858
MAX_LATENCY=997109
P50_LATENCY=305000
P90_LATENCY=550000
P99_LATENCY=710000
MEAN_LATENCY=376989.12
STDDEV_LATENCY=184046.92
After patch :
RT_LATENCY,MIN_LATENCY,MAX_LATENCY,P50_LATENCY,P90_LATENCY,P99_LATENCY,MEAN_LATENCY,STDDEV_LATENCY MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET
to 7.7.7.84 () port 0 AF_INET : first burst 0 : cpu bind
RT_LATENCY=40545.492
MIN_LATENCY=9834
MAX_LATENCY=78366
P50_LATENCY=33583
P90_LATENCY=59000
P99_LATENCY=69000
MEAN_LATENCY=38364.67
STDDEV_LATENCY=12865.26
--- kernel/softirq.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
kernel/softirq.c +9 -8
@@ -194,21 +194,21 @@ void local_bh_enable_ip(unsigned long ip)EXPORT_SYMBOL (local_bh_enable_ip);/* -* We restart softirq processing MAX_SOFTIRQ_RESTART times, -* and we fall back to softirqd after that. +* We restart softirq processing for at most 2 ms, +* and if need_resched() is not set. * -* This number has been established via experimentation. +* These limits have been established via experimentation. * The two things to balance is latency against fairness - * we want to handle softirqs as soon as possible, but they * should not be able to lock up the box. */ -#define MAX_SOFTIRQ_RESTART 10 +#define MAX_SOFTIRQ_TIME msecs_to_jiffies(2) asmlinkage void __do_softirq(void) {struct softirq_action *h;__u32 pending; -int max_restart =MAX_SOFTIRQ_RESTART ; +unsigned long end = jiffies +MAX_SOFTIRQ_TIME ;int cpu; pending =local_softirq_pending ();
@@ -253,11 +253,12 @@ restart:local_irq_disable (); pending =local_softirq_pending (); -if (pending && --max_restart) - goto restart; +if (pending) { +if (time_before (jiffies, end) && !need_resched ()) + goto restart; -if (pending)wakeup_softirqd (); + }lockdep_softirq_exit ();
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
Re: [ 146/184] softirq: reduce latencies
Hi Willy,
This patch introduced a bug, which was then fixed by commit 34376a50fb1f ("Fix lockup related to stop_machine being stuck in __do_softirq."), do we need this fix for 2.6.32 ?
Quoted 68 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
In various network workloads,
__do_softirq()latencies can be up to 20 ms if HZ=1000, and 200 ms if HZ=100.This is because we iterate 10 times in the softirq dispatcher, and some actions can consume a lot of cycles.
This patch changes the fallback to ksoftirqd condition to :
- A time limit of 2 ms.
-need_resched()being set on current taskWhen one of this condition is met, we wakeup ksoftirqd for further softirq processing if we still have pending softirqs.
Using
need_resched()as the only condition can trigger RCU stalls, as we can keep BH disabled for too long.I ran several benchmarks and got no significant difference in throughput, but a very significant reduction of latencies (one order of magnitude) :
In following bench, 200 antagonist "netperf -t
TCP_RR" are started in background, using all available cpus.Then we start one "netperf -t
TCP_RR", bound to the cpu handling the NIC IRQ (hard+soft)Before patch :
RT_LATENCY,MIN_LATENCY,MAX_LATENCY,P50_LATENCY,P90_LATENCY,P99_LATENCY,MEAN_LATENCY,STDDEV_LATENCY MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0
AF_INET
to 7.7.7.84 () port 0AF_INET: first burst 0 : cpu bind
RT_LATENCY=550110.424
MIN_LATENCY=146858
MAX_LATENCY=997109
P50_LATENCY=305000
P90_LATENCY=550000
P99_LATENCY=710000
MEAN_LATENCY=376989.12
STDDEV_LATENCY=184046.92After patch :
RT_LATENCY,MIN_LATENCY,MAX_LATENCY,P50_LATENCY,P90_LATENCY,P99_LATENCY,MEAN_LATENCY,STDDEV_LATENCY MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0
AF_INET
to 7.7.7.84 () port 0AF_INET: first burst 0 : cpu bind
RT_LATENCY=40545.492
MIN_LATENCY=9834
MAX_LATENCY=78366
P50_LATENCY=33583
P90_LATENCY=59000
P99_LATENCY=69000
MEAN_LATENCY=38364.67
STDDEV_LATENCY=12865.26Signed-off-by Eric Dumazet <[email protected]>Cc David Miller <[email protected]>Cc Tom Herbert <[email protected]>Cc Ben Hutchings <[email protected]>Signed-off-by David S. Miller <[email protected]>(cherry picked from commit c10d73671ad30f54692f7f69f0e09e75d3a8926a)Signed-off-by Willy Tarreau <[email protected]>
Re: [ 146/184] softirq: reduce latencies
Hi Li,
I just found your mail unread in my box by pure luck, I'm sorry.
Quoted 8 lines · Li Zefan
Cc Ben GreearCc TejunHi Willy,
This patch introduced a bug, which was then fixed by commit
34376a50fb1f("Fix lockup related tostop_machinebeing stuck in__do_softirq."), do we need this fix for 2.6.32 ?
Yes, I just checked the code and in doubt I think it's safer to apply it as well. So I'm queuing the fix for .62, thanks !
Willy
PATCH 147/184 af_packet: remove BUG statement in
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
tpacket_destruct_skb
From: "[email protected]" <[email protected]>
[ Upstream commit 7f5c3e3a80e6654cf48dfba7cf94f88c6b505467 ]
Here's a quote of the comment about the BUG macro from asm-generic/bug.h:
Don't use BUG() or BUG_ON() unless there's really no way out; one example might be detecting data structure corruption in the middle of an operation that can't be backed out of. If the (sub)system can somehow continue operating, perhaps with reduced functionality, it's probably not BUG-worthy.
If you're tempted to BUG(), think again: is completely giving up really the *only* solution? There are usually better options, where users don't need to reboot ASAP and can mostly shut down cleanly.
In our case, the status flag of a ring buffer slot is managed from both sides, the kernel space and the user space. This means that even though the kernel side might work as expected, the user space screws up and changes this flag right between the send(2) is triggered when the flag is changed to TP_STATUS_SENDING and a given skb is destructed after some time. Then, this will hit the BUG macro. As David suggested, the best solution is to simply remove this statement since it cannot be used for kernel side internal consistency checks. I've tested it and the system still behaves /stable/ in this case, so in accordance with the above comment, we should rather remove it.
--- net/packet/af_packet.c | 1 - 1 file changed, 1 deletion(-)
net/packet/af_packet.c +0 -1
@@ -828,7 +828,6 @@ static void tpacket_destruct_skb(struct sk_buff *skb)if (likely (po->tx_ring.pg_vec)) { ph =skb_shinfo (skb)->destructor_arg; -BUG_ON (__packet_get_status (po, ph) !=TP_STATUS_SENDING );BUG_ON (atomic_read (&po->tx_ring.pending) ==0 );atomic_dec (&po->tx_ring.pending);__packet_set_status (po, ph,TP_STATUS_AVAILABLE );
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 148/184 bridge: set priority of STP packets
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Stephen Hemminger <[email protected]>
Spanning Tree Protocol packets should have always been marked as control packets, this causes them to get queued in the high prirority FIFO. As Radia Perlman mentioned in her LCA talk, STP dies if bridge gets overloaded and can't communicate. This is a long-standing bug back to the first versions of Linux bridge.
--- net/bridge/br_stp_bpdu.c | 2 ++ 1 file changed, 2 insertions(+)
net/bridge/br_stp_bpdu.c +2 -0
@@ -15,6 +15,7 @@#include <linux/netfilter_bridge.h> #include <linux/etherdevice.h> #include <linux/llc.h> +#include <linux/pkt_sched.h> #include <net/net_namespace.h> #include <net/llc.h> #include <net/llc_pdu.h>
@@ -39,6 +40,7 @@ static void br_send_bpdu(struct net_bridge_port *p, skb->dev = p->dev; skb->protocol =htons (ETH_P_802_2 ); + skb->priority =TC_PRIO_CONTROL ;skb_reserve (skb,LLC_RESERVE );memcpy (__skb_put (skb, length), data, length);
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 149/184 bonding: Fix slave selection bug.
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Hillf Danton <[email protected]>
The returned slave is incorrect, if the net device under check is not charged yet by the master.
--- drivers/net/bonding/bonding.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/bonding/bonding.h +2 -2
@@ -236,11 +236,11 @@ static inline struct slave *bond_get_slave_by_dev(struct bonding *bond, struct nbond_for_each_slave (bond, slave, i) {if (slave->dev == slave_dev) { -break ; +return slave; } } -return slave; +return 0 ; }static inline struct bonding *bond_get_bond_by_slave (struct slave *slave)
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 150/184 ipv4: check rt_genid in dst_check
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin LaHaise <[email protected]>
commit d11a4dc18bf41719c9f0d7ed494d295dd2973b92
Author: Timo Ter�s <[email protected]> Date: Thu Mar 18 23:20:20 2010 +0000
ipv4: check rt_genid in dst_check
Xfrm_dst keeps a reference to ipv4 rtable entries on each cached bundle. The only way to renew xfrm_dst when the underlying route has changed, is to implement dst_check for this. This is what ipv6 side does too.
The problems started after 87c1e12b5eeb7b30b4b41291bef8e0b41fc3dde9 ("ipsec: Fix bogus bundle flowi") which fixed a bug causing xfrm_dst to not get reused, until that all lookups always generated new xfrm_dst with new route reference and path mtu worked. But after the fix, the old routes started to get reused even after they were expired causing pmtu to break (well it would occationally work if the rtable gc had run recently and marked the route obsolete causing dst_check to get called).
This commit is based on the above, with the addition of verifying blackhole routes in the same manner.
--- net/ipv4/route.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)
net/ipv4/route.c +12 -5
@@ -1412,7 +1412,7 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,dev_hold (rt->u.dst.dev);if (rt->idev)in_dev_hold (rt->idev); - rt->u.dst.obsolete =0 ; + rt->u.dst.obsolete =-1 ; rt->u.dst.lastuse = jiffies; rt->u.dst.path = &rt->u.dst; rt->u.dst.neighbour =NULL ;
@@ -1477,7 +1477,7 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)struct dst_entry *ret = dst;if (rt) { -if (dst->obsolete) { +if (dst->obsolete >0 ) {ip_rt_put (rt); ret =NULL ; } else if ((rt->rt_flags & RTCF_REDIRECTED) ||
@@ -1700,7 +1700,9 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu)static struct dst_entry *ipv4_dst_check (struct dst_entry *dst,u32 cookie) { -return NULL ; +if (rt_is_expired ((struct rtable *)dst)) +return NULL ; +return dst; }static void ipv4_dst_destroy (struct dst_entry *dst)
@@ -1862,7 +1864,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,if (!rth) goto e_nobufs; - rth->u.dst.output= ip_rt_bug; + rth->u.dst.output = ip_rt_bug; + rth->u.dst.obsolete =-1 ;atomic_set (&rth->u.dst.__refcnt,1 ); rth->u.dst.flags=DST_HOST ;
@@ -2023,6 +2026,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->fl.oif =0 ; rth->rt_spec_dst= spec_dst; + rth->u.dst.obsolete =-1 ; rth->u.dst.input = ip_forward; rth->u.dst.output = ip_output; rth->rt_genid =rt_genid (dev_net (rth->u.dst.dev));
@@ -2187,6 +2191,7 @@ local_input: goto e_nobufs; rth->u.dst.output= ip_rt_bug; + rth->u.dst.obsolete =-1 ; rth->rt_genid =rt_genid (net);atomic_set (&rth->u.dst.__refcnt,1 );
@@ -2411,7 +2416,8 @@ static int __mkroute_output(struct rtable **result, rth->rt_gateway = fl->fl4_dst; rth->rt_spec_dst= fl->fl4_src; - rth->u.dst.output=ip_output; + rth->u.dst.output = ip_output; + rth->u.dst.obsolete =-1 ; rth->rt_genid =rt_genid (dev_net (dev_out));RT_CACHE_STAT_INC (out_slow_tot);
@@ -2741,6 +2747,7 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowiif (rt) {struct dst_entry *new = &rt->u.dst; + new->obsolete =-1 ;atomic_set (&new->__refcnt,1 ); new->__use =1 ; new->input = dst_discard;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
Re: [ 150/184] ipv4: check rt_genid in dst_check
Quoted 26 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin LaHaise <[email protected]>
commit
d11a4dc18bf41719c9f0d7ed494d295dd2973b92Author: Timo Ters <[email protected]> Date: Thu Mar 18 23:20:20 2010 +0000ipv4: check
rt_genidindst_check
Xfrm_dstkeeps a reference to ipv4 rtable entries on each cached bundle. The only way to renewxfrm_dstwhen the underlying route has changed, is to implementdst_checkfor this. This is what ipv6 side does too.The problems started after
87c1e12b5eeb7b30b4b41291bef8e0b41fc3dde9("ipsec: Fix bogus bundle flowi") which fixed a bug causingxfrm_dstto not get reused, until that all lookups always generated newxfrm_dstwith new route reference and path mtu worked. But after the fix, the old routes started to get reused even after they were expired causing pmtu to break (well it would occationally work if the rtable gc had run recently and marked the route obsolete causingdst_checkto get called).
Quoted 6 lines
Signed-off-by Timo Teras <[email protected]>Acked-by Herbert Xu <[email protected]>Signed-off-by David S. Miller <[email protected]>This commit is based on the above, with the addition of verifying blackhole routes in the same manner.
That addition doesn't seem to correspond to anything in mainline. Why should 2.6.32 differ?
Ben.
Quoted 83 lines
Signed-off-by Benjamin LaHaise <[email protected]>Signed-off-by Willy Tarreau <[email protected]>--- net/ipv4/route.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)net/ipv4/route.c +12
-5@@ -1412,7 +1412,7 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,dev_hold (rt->u.dst.dev);if (rt->idev)in_dev_hold (rt->idev); - rt->u.dst.obsolete =0 ; + rt->u.dst.obsolete =-1 ; rt->u.dst.lastuse = jiffies; rt->u.dst.path = &rt->u.dst; rt->u.dst.neighbour =NULL ;@@ -1477,7 +1477,7 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)struct dst_entry *ret = dst;if (rt) { -if (dst->obsolete) { +if (dst->obsolete >0 ) {ip_rt_put (rt); ret =NULL ; } else if ((rt->rt_flags & RTCF_REDIRECTED) ||@@ -1700,7 +1700,9 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu)static struct dst_entry *ipv4_dst_check (struct dst_entry *dst,u32 cookie) { -return NULL ; +if (rt_is_expired ((struct rtable *)dst)) +return NULL ; +return dst; }static void ipv4_dst_destroy (struct dst_entry *dst)@@ -1862,7 +1864,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,if (!rth) goto e_nobufs; - rth->u.dst.output= ip_rt_bug; + rth->u.dst.output = ip_rt_bug; + rth->u.dst.obsolete =-1 ;atomic_set (&rth->u.dst.__refcnt,1 ); rth->u.dst.flags=DST_HOST ;@@ -2023,6 +2026,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->fl.oif =0 ; rth->rt_spec_dst= spec_dst; + rth->u.dst.obsolete =-1 ; rth->u.dst.input = ip_forward; rth->u.dst.output = ip_output; rth->rt_genid =rt_genid (dev_net (rth->u.dst.dev));@@ -2187,6 +2191,7 @@ local_input: goto e_nobufs; rth->u.dst.output= ip_rt_bug; + rth->u.dst.obsolete =-1 ; rth->rt_genid =rt_genid (net);atomic_set (&rth->u.dst.__refcnt,1 );@@ -2411,7 +2416,8 @@ static int __mkroute_output(struct rtable **result, rth->rt_gateway = fl->fl4_dst; rth->rt_spec_dst= fl->fl4_src; - rth->u.dst.output=ip_output; + rth->u.dst.output = ip_output; + rth->u.dst.obsolete =-1 ; rth->rt_genid =rt_genid (dev_net (dev_out));RT_CACHE_STAT_INC (out_slow_tot);@@ -2741,6 +2747,7 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowiif (rt) {struct dst_entry *new = &rt->u.dst; + new->obsolete =-1 ;atomic_set (&new->__refcnt,1 ); new->__use =1 ; new->input = dst_discard;
Signature · 3 lines
Ben Hutchings
Theory and practice are closer in theory than in practice.
- John Levine, moderator of comp.compilersRe: [ 150/184] ipv4: check rt_genid in dst_check
Quoted 5 lines · Ben Hutchings
Quoted 2 lines
This commit is based on the above, with the addition of verifying blackhole routes in the same manner.
That addition doesn't seem to correspond to anything in mainline. Why should 2.6.32 differ?
Fixing the issue with blackhole routes as it was accomplished in mainline would require pulling in a lot more code, and people were not interested in pulling in all of the dependencies given the much higher risk of trying to select the right subset of changes to include. The addition of the single line of "dst->obsolete = -1;" in ipv4_dst_blackhole() was much easier to verify, and is in the spirit of the patch in question. This is the minimal set of changes to fix the bug in question.
-ben
Quoted 90 lines
Ben.
Quoted 83 lines
Signed-off-by Benjamin LaHaise <[email protected]>Signed-off-by Willy Tarreau <[email protected]>--- net/ipv4/route.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)net/ipv4/route.c +12
-5@@ -1412,7 +1412,7 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,dev_hold (rt->u.dst.dev);if (rt->idev)in_dev_hold (rt->idev); - rt->u.dst.obsolete =0 ; + rt->u.dst.obsolete =-1 ; rt->u.dst.lastuse = jiffies; rt->u.dst.path = &rt->u.dst; rt->u.dst.neighbour =NULL ;@@ -1477,7 +1477,7 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)struct dst_entry *ret = dst;if (rt) { -if (dst->obsolete) { +if (dst->obsolete >0 ) {ip_rt_put (rt); ret =NULL ; } else if ((rt->rt_flags & RTCF_REDIRECTED) ||@@ -1700,7 +1700,9 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu)static struct dst_entry *ipv4_dst_check (struct dst_entry *dst,u32 cookie) { -return NULL ; +if (rt_is_expired ((struct rtable *)dst)) +return NULL ; +return dst; }static void ipv4_dst_destroy (struct dst_entry *dst)@@ -1862,7 +1864,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,if (!rth) goto e_nobufs; - rth->u.dst.output= ip_rt_bug; + rth->u.dst.output = ip_rt_bug; + rth->u.dst.obsolete =-1 ;atomic_set (&rth->u.dst.__refcnt,1 ); rth->u.dst.flags=DST_HOST ;@@ -2023,6 +2026,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->fl.oif =0 ; rth->rt_spec_dst= spec_dst; + rth->u.dst.obsolete =-1 ; rth->u.dst.input = ip_forward; rth->u.dst.output = ip_output; rth->rt_genid =rt_genid (dev_net (rth->u.dst.dev));@@ -2187,6 +2191,7 @@ local_input: goto e_nobufs; rth->u.dst.output= ip_rt_bug; + rth->u.dst.obsolete =-1 ; rth->rt_genid =rt_genid (net);atomic_set (&rth->u.dst.__refcnt,1 );@@ -2411,7 +2416,8 @@ static int __mkroute_output(struct rtable **result, rth->rt_gateway = fl->fl4_dst; rth->rt_spec_dst= fl->fl4_src; - rth->u.dst.output=ip_output; + rth->u.dst.output = ip_output; + rth->u.dst.obsolete =-1 ; rth->rt_genid =rt_genid (dev_net (dev_out));RT_CACHE_STAT_INC (out_slow_tot);@@ -2741,6 +2747,7 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowiif (rt) {struct dst_entry *new = &rt->u.dst; + new->obsolete =-1 ;atomic_set (&new->__refcnt,1 ); new->__use =1 ; new->input = dst_discard;Signature · 3 lines
Ben Hutchings Theory and practice are closer in theory than in practice. - John Levine, moderator of comp.compilers
Signature · 1 line
"Thought is the essence of where you are now."
Re: [ 150/184] ipv4: check rt_genid in dst_check
Quoted 7 lines · Benjamin LaHaise
Quoted 5 lines · Ben Hutchings
Quoted 2 lines
This commit is based on the above, with the addition of verifying blackhole routes in the same manner.
That addition doesn't seem to correspond to anything in mainline. Why should 2.6.32 differ?
Quoted 7 lines
Fixing the issue with blackhole routes as it was accomplished in mainline would require pulling in a lot more code, and people were not interested in pulling in all of the dependencies given the much higher risk of trying to select the right subset of changes to include. The addition of the single line of "
dst->obsolete= -1;" inipv4_dst_blackhole()was much easier to verify, and is in the spirit of the patch in question. This is the minimal set of changes to fix the bug in question.
Thank you Ben, I'll add this description to the existing commit message.
Best regards,
Willy
Re: [ 150/184] ipv4: check rt_genid in dst_check
Hi Willy,
Quoted 16 lines · Willy Tarreau
Quoted 7 lines · Benjamin LaHaise
Quoted 5 lines · Ben Hutchings
Quoted 2 lines
This commit is based on the above, with the addition of verifying blackhole routes in the same manner.
That addition doesn't seem to correspond to anything in mainline. Why should 2.6.32 differ?
Quoted 7 lines
Fixing the issue with blackhole routes as it was accomplished in mainline would require pulling in a lot more code, and people were not interested in pulling in all of the dependencies given the much higher risk of trying to select the right subset of changes to include. The addition of the single line of "
dst->obsolete= -1;" inipv4_dst_blackhole()was much easier to verify, and is in the spirit of the patch in question. This is the minimal set of changes to fix the bug in question.
Quoted 1 line
Thank you Ben, I'll add this description to the existing commit message.
A link to the test case for this issue might be helpful to include as well. It is at http://marc.info/?l=linux-netdev&m=135015076708950&w=2 . Cheers,
-ben
Quoted 2 lines
Best regards,
Willy
Signature · 1 line
"Thought is the essence of where you are now."
PATCH 151/184 net_sched: gact: Fix potential panic in tcf_gact().
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Hiroaki SHIMODA <[email protected]>
[ Upstream commit 696ecdc10622d86541f2e35cc16e15b6b3b1b67e ]
gact_rand array is accessed by gact->tcfg_ptype whose value is assumed to less than MAX_RAND, but any range checks are not performed.
So add a check in tcf_gact_init(). And in tcf_gact(), we can reduce a branch.
--- net/sched/act_gact.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
net/sched/act_gact.c +11 -3
@@ -67,6 +67,9 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,struct tcf_common *pc;int ret =0 ;int err; +#ifdef CONFIG_GACT_PROB +struct tc_gact_p *p_parm =NULL ; +#endif if (nla ==NULL )return -EINVAL ;
@@ -82,6 +85,12 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,#ifndef CONFIG_GACT_PROB if (tb[TCA_GACT_PROB ] !=NULL )return -EOPNOTSUPP ; +#else +if (tb[TCA_GACT_PROB ]) { + p_parm =nla_data (tb[TCA_GACT_PROB ]); +if (p_parm->ptype >=MAX_RAND ) +return -EINVAL ; + }#endif pc =tcf_hash_check (parm->index, a, bind, &gact_hash_info);
@@ -103,8 +112,7 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,spin_lock_bh (&gact->tcf_lock); gact->tcf_action = parm->action;#ifdef CONFIG_GACT_PROB -if (tb[TCA_GACT_PROB ] !=NULL ) { -struct tc_gact_p *p_parm =nla_data (tb[TCA_GACT_PROB ]); +if (p_parm) { gact->tcfg_paction = p_parm->paction; gact->tcfg_pval = p_parm->pval; gact->tcfg_ptype = p_parm->ptype;
@@ -132,7 +140,7 @@ static int tcf_gact(struct sk_buff *skb, struct tc_action *a, struct tcf_resultspin_lock (&gact->tcf_lock);#ifdef CONFIG_GACT_PROB -if (gact->tcfg_ptype && gact_rand[gact->tcfg_ptype] !=NULL ) +if (gact->tcfg_ptype) action = gact_rand[gact->tcfg_ptype](gact);else action = gact->tcf_action;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 152/184 net: sched: integer overflow fix
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Hasko <[email protected]>
[ Upstream commit d2fe85da52e89b8012ffad010ef352a964725d5f ]
Fixed integer overflow in function htb_dequeue
--- net/sched/sch_htb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_htb.c +1 -1
@@ -865,7 +865,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) q->now =psched_get_time (); start_at = jiffies; - next_event = q->now +5 *PSCHED_TICKS_PER_SEC ; + next_event = q->now +5LLU *PSCHED_TICKS_PER_SEC ;for (level =0 ; level <TC_HTB_MAXDEPTH ; level++) {/* common case optimization - skip event handler quickly */
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 153/184 net: prevent setting ttl=0 via IP_TTL
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Cong Wang <[email protected]>
[ Upstream commit c9be4a5c49cf51cc70a993f004c5bb30067a65ce ]
A regression is introduced by the following commit:
commit 4d52cfbef6266092d535237ba5a4b981458ab171 Author: Eric Dumazet <[email protected]> Date: Tue Jun 2 00:42:16 2009 -0700 net: ipv4/ip_sockglue.c cleanups Pure cleanups
but it is not a pure cleanup...
- if (val != -1 && (val < 1 || val>255)) + if (val != -1 && (val < 0 || val > 255))
Since there is no reason provided to allow ttl=0, change it back.
--- net/ipv4/ip_sockglue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/ip_sockglue.c +1 -1
@@ -563,7 +563,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,case IP_TTL :if (optlen <1 ) goto e_inval; -if (val !=-1 && (val <0 || val >255 )) +if (val !=-1 && (val <1 || val >255 )) goto e_inval; inet->uc_ttl = val;break ;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 154/184 net: fix divide by zero in tcp algorithm illinois
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Jesper Dangaard Brouer <[email protected]>
commit 8f363b77ee4fbf7c3bbcf5ec2c5ca482d396d664 upstream
Reading TCP stats when using TCP Illinois congestion control algorithm can cause a divide by zero kernel oops.
The division by zero occur in tcp_illinois_info() at:
do_div(t, ca->cnt_rtt);
where ca->cnt_rtt can become zero (when rtt_reset is called)
Steps to Reproduce:
1. Register tcp_illinois:
# sysctl -w net.ipv4.tcp_congestion_control=illinois
2. Monitor internal TCP information via command "ss -i"
# watch -d ss -i
3. Establish new TCP conn to machineEither it fails at the initial conn, or else it needs to wait for a loss or a reset.
This is only related to reading stats. The function avg_delay() also performs the same divide, but is guarded with a (ca->cnt_rtt > 0) at its calling point in update_params(). Thus, simply fix tcp_illinois_info().
Function tcp_illinois_info() / get_info() is called without socket lock. Thus, eliminate any race condition on ca->cnt_rtt by using a local stack variable. Simply reuse info.tcpv_rttcnt, as its already set to ca->cnt_rtt.
Function avg_delay() is not affected by this race condition, as its called with the socket lock.
--- net/ipv4/tcp_illinois.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
net/ipv4/tcp_illinois.c +5 -3
@@ -313,11 +313,13 @@ static void tcp_illinois_info(struct sock *sk, u32 ext, .tcpv_rttcnt = ca->cnt_rtt, .tcpv_minrtt = ca->base_rtt, }; -u64 t = ca->sum_rtt; -do_div (t, ca->cnt_rtt); - info.tcpv_rtt = t; +if (info.tcpv_rttcnt >0 ) { +u64 t = ca->sum_rtt; +do_div (t, info.tcpv_rttcnt); + info.tcpv_rtt = t; + }nla_put (skb,INET_DIAG_VEGASINFO ,sizeof (info), &info); } }
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 155/184 net: guard tcp_set_keepalive() to tcp sockets
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 3e10986d1d698140747fcfc2761ec9cb64c1d582 ]
Its possible to use RAW sockets to get a crash intcp_set_keepalive() / sk_reset_timer()
Fix is to make sure socket is a SOCK_STREAM one.
--- net/core/sock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/core/sock.c +2 -1
@@ -562,7 +562,8 @@ set_rcvbuf:case SO_KEEPALIVE :#ifdef CONFIG_INET -if (sk->sk_protocol ==IPPROTO_TCP ) +if (sk->sk_protocol ==IPPROTO_TCP && + sk->sk_type ==SOCK_STREAM )tcp_set_keepalive (sk, valbool);#endif sock_valbool_flag (sk,SOCK_KEEPOPEN , valbool);
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 156/184 net: fix info leak in compat dev_ifconf()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
commit 43da5f2e0d0c69ded3d51907d9552310a6b545e8 upstream.
The implementation of dev_ifconf() for the compat ioctl interface uses an intermediate ifc structure allocated in userland for the duration of the syscall. Though, it fails to initialize the padding bytes inserted for alignment and that for leaks four bytes of kernel stack. Add an explicit memset(0) before filling the structure to avoid the info leak.
--- fs/compat_ioctl.c | 1 + 1 file changed, 1 insertion(+)
fs/compat_ioctl.c +1 -0
@@ -352,6 +352,7 @@ static int dev_ifconf(unsigned int fd, unsigned int cmd, unsigned long arg)if (copy_from_user (&ifc32,compat_ptr (arg),sizeof (struct ifconf32 )))return -EFAULT ; +memset (&ifc,0 ,sizeof (ifc));if (ifc32.ifcbuf ==0 ) { ifc32.ifc_len =0 ; ifc.ifc_len =0 ;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 157/184 inet: add RCU protection to inet->opt
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit f6d8bd051c391c1c0458a30b2a7abcd939329259 upstream.
We lack proper synchronization to manipulate inet->opt ip_options
Problem is ip_make_skb() calls ip_setup_cork() and ip_setup_cork() possibly makes a copy of ipc->opt (struct ip_options), without any protection against another thread manipulating inet->opt.
Another thread can change inet->opt pointer and free old one under us.
Use RCU to protect inet->opt (changed to inet->inet_opt).
Instead of handling atomic refcounts, just copy ip_options when necessary, to avoid cache line dirtying.
We cant insert an rcu_head in struct ip_options since its included in skb->cb[], so this patch is large because I had to introduce a new ip_options_rcu structure.
--- include/net/inet_sock.h | 14 +++-- include/net/ip.h | 11 ++-- net/dccp/ipv4.c | 15 +++--- net/dccp/ipv6.c | 2 +- net/ipv4/af_inet.c | 16 ++++-- net/ipv4/cipso_ipv4.c | 113 ++++++++++++++++++++++------------------ net/ipv4/icmp.c | 23 ++++---- net/ipv4/inet_connection_sock.c | 8 +-- net/ipv4/ip_options.c | 38 +++++++------- net/ipv4/ip_output.c | 50 +++++++++--------- net/ipv4/ip_sockglue.c | 33 ++++++++---- net/ipv4/raw.c | 19 +++++-- net/ipv4/syncookies.c | 4 +- net/ipv4/tcp_ipv4.c | 33 +++++++----- net/ipv4/udp.c | 21 ++++++-- net/ipv6/tcp_ipv6.c | 2 +- 16 files changed, 235 insertions(+), 167 deletions(-)
include/net/inet_sock.h +11 -3
@@ -56,7 +56,15 @@ struct ip_options {unsigned char __data[0 ]; }; -#define optlength (opt) (sizeof(struct ip_options) + opt->optlen) +struct ip_options_rcu { +struct rcu_head rcu; +struct ip_options opt; +}; + +struct ip_options_data { +struct ip_options_rcu opt; +char data[40 ]; +};struct inet_request_sock {struct request_sock req;
@@ -77,7 +85,7 @@ struct inet_request_sock { acked :1 , no_srccheck: 1;kmemcheck_bitfield_end (flags); -struct ip_options *opt; +struct ip_options_rcu *opt; };static inline struct inet_request_sock *inet_rsk (const struct request_sock *sk)
@@ -122,7 +130,7 @@ struct inet_sock {__be32 saddr;__s16 uc_ttl;__u16 cmsg_flags; -struct ip_options *opt; +struct ip_options_rcu *inet_opt;__be16 sport;__u16 id;__u8 tos;
include/net/ip.h +6 -5
@@ -54,7 +54,7 @@ struct ipcm_cookie {__be32 addr;int oif; -struct ip_options *opt; +struct ip_options_rcu *opt;union skb_shared_tx shtx; };
@@ -92,7 +92,7 @@ extern int igmp_mc_proc_init(void);extern int ip_build_and_send_pkt (struct sk_buff *skb,struct sock *sk,__be32 saddr,__be32 daddr, -struct ip_options *opt); +struct ip_options_rcu *opt);extern int ip_rcv (struct sk_buff *skb,struct net_device *dev,struct packet_type *pt,struct net_device *orig_dev);extern int ip_local_deliver (struct sk_buff *skb);
@@ -362,14 +362,15 @@ extern int ip_forward(struct sk_buff *skb);* Functions provided by ip_options.c */ -extern void ip_options_build (struct sk_buff *skb,struct ip_options *opt,__be32 daddr,struct rtable *rt,int is_frag); +extern void ip_options_build (struct sk_buff *skb,struct ip_options *opt, +__be32 daddr,struct rtable *rt,int is_frag);extern int ip_options_echo (struct ip_options *dopt,struct sk_buff *skb);extern void ip_options_fragment (struct sk_buff *skb);extern int ip_options_compile (struct net *net,struct ip_options *opt,struct sk_buff *skb); -extern int ip_options_get (struct net *net,struct ip_options **optp, +extern int ip_options_get (struct net *net,struct ip_options_rcu **optp,unsigned char *data,int optlen); -extern int ip_options_get_from_user (struct net *net,struct ip_options **optp, +extern int ip_options_get_from_user (struct net *net,struct ip_options_rcu **optp,unsigned char __user *data,int optlen);extern void ip_options_undo (struct ip_options * opt);extern void ip_forward_options (struct sk_buff *skb);
net/dccp/ipv4.c +9 -6
@@ -47,6 +47,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)__be32 daddr, nexthop;int tmp;int err; +struct ip_options_rcu *inet_opt; dp->dccps_role =DCCP_ROLE_CLIENT ;
@@ -57,10 +58,12 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)return -EAFNOSUPPORT ; nexthop = daddr = usin->sin_addr.s_addr; -if (inet->opt !=NULL && inet->opt->srr) { + + inet_opt = inet->inet_opt; +if (inet_opt !=NULL && inet_opt->opt.srr) {if (daddr ==0 )return -EINVAL ; - nexthop = inet->opt->faddr; + nexthop = inet_opt->opt.faddr; } tmp = ip_route_connect(&rt, nexthop, inet->saddr,
@@ -75,7 +78,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)return -ENETUNREACH ; } -if (inet->opt ==NULL || !inet->opt->srr) +if (inet_opt ==NULL || !inet_opt->opt.srr) daddr = rt->rt_dst;if (inet->saddr ==0 )
@@ -86,8 +89,8 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->daddr = daddr;inet_csk (sk)->icsk_ext_hdr_len =0 ; -if (inet->opt !=NULL ) -inet_csk (sk)->icsk_ext_hdr_len = inet->opt->optlen; +if (inet_opt) +inet_csk (sk)->icsk_ext_hdr_len = inet_opt->opt.optlen;/* * Socket identity is still unknown (sport may be zero). * However we set state to DCCP_REQUESTING and not releasing socket
@@ -397,7 +400,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, newinet->daddr = ireq->rmt_addr; newinet->rcv_saddr = ireq->loc_addr; newinet->saddr = ireq->loc_addr; - newinet->opt = ireq->opt; + newinet->inet_opt = ireq->opt; ireq->opt =NULL ; newinet->mc_index =inet_iif (skb); newinet->mc_ttl =ip_hdr (skb)->ttl;
net/dccp/ipv6.c +1 -1
@@ -600,7 +600,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,First: no IPv4 options. */ - newinet->opt =NULL ; + newinet->inet_opt =NULL ;/* Clone RX bits */ newnp->rxopt.all = np->rxopt.all;
net/ipv4/af_inet.c +11 -5
@@ -152,7 +152,7 @@ void inet_sock_destruct(struct sock *sk)WARN_ON (sk->sk_wmem_queued);WARN_ON (sk->sk_forward_alloc); -kfree (inet->opt); +kfree (inet->inet_opt);dst_release (sk->sk_dst_cache);sk_refcnt_debug_dec (sk); }
@@ -1065,9 +1065,11 @@ static int inet_sk_reselect_saddr(struct sock *sk)__be32 old_saddr = inet->saddr;__be32 new_saddr;__be32 daddr = inet->daddr; +struct ip_options_rcu *inet_opt; -if (inet->opt && inet->opt->srr) - daddr = inet->opt->faddr; + inet_opt = inet->inet_opt; +if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr;/* Query new route. */ err = ip_route_connect(&rt, daddr, 0,
@@ -1109,6 +1111,7 @@ int inet_sk_rebuild_header(struct sock *sk)struct inet_sock *inet =inet_sk (sk);struct rtable *rt = (struct rtable *)__sk_dst_check (sk,0 );__be32 daddr; +struct ip_options_rcu *inet_opt;int err;/* Route is OK, nothing to do. */
@@ -1116,9 +1119,12 @@ int inet_sk_rebuild_header(struct sock *sk)return 0 ;/* Reroute. */ +rcu_read_lock (); + inet_opt =rcu_dereference (inet->inet_opt); daddr = inet->daddr; -if (inet->opt && inet->opt->srr) - daddr = inet->opt->faddr; +if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr; +rcu_read_unlock (); {struct flowi fl = { .oif = sk->sk_bound_dev_if,
net/ipv4/cipso_ipv4.c +63 -50
216 lines not on this page · raw message
net/ipv4/icmp.c +11 -12
@@ -107,8 +107,7 @@ struct icmp_bxm {__be32 times[3 ]; } data;int head_len; -struct ip_options replyopts; -unsigned char optbuf[40 ]; +struct ip_options_data replyopts; };/* An array of errno for error messages from dest unreach. */
@@ -362,7 +361,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)struct inet_sock *inet;__be32 daddr; -if (ip_options_echo (&icmp_param->replyopts, skb)) +if (ip_options_echo (&icmp_param->replyopts.opt.opt, skb))return ; sk =icmp_xmit_lock (net);
@@ -376,10 +375,10 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) daddr = ipc.addr = rt->rt_src; ipc.opt =NULL ; ipc.shtx.flags =0 ; -if (icmp_param->replyopts.optlen) { - ipc.opt = &icmp_param->replyopts; -if (ipc.opt->srr) - daddr = icmp_param->replyopts.faddr; +if (icmp_param->replyopts.opt.opt.optlen) { + ipc.opt = &icmp_param->replyopts.opt; +if (ipc.opt->opt.srr) + daddr = icmp_param->replyopts.opt.opt.faddr; } { struct flowi fl = { .nl_u = { .ip4_u =
@@ -516,7 +515,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) IPTOS_PREC_INTERNETCONTROL) : iph->tos; -if (ip_options_echo (&icmp_param.replyopts, skb_in)) +if (ip_options_echo (&icmp_param.replyopts.opt.opt, skb_in)) goto out_unlock;
@@ -532,15 +531,15 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) icmp_param.offset =skb_network_offset (skb_in);inet_sk (sk)->tos = tos; ipc.addr = iph->saddr; - ipc.opt = &icmp_param.replyopts; + ipc.opt = &icmp_param.replyopts.opt; ipc.shtx.flags =0 ; {struct flowi fl = { .nl_u = { .ip4_u = { - .daddr = icmp_param.replyopts.srr ? - icmp_param.replyopts.faddr : + .daddr = icmp_param.replyopts.opt.opt.srr ? + icmp_param.replyopts.opt.opt.faddr : iph->saddr, .saddr = saddr, .tos =RT_TOS (tos)
@@ -629,7 +628,7 @@ route_done: room =dst_mtu (&rt->u.dst);if (room >576 ) room =576 ; - room -=sizeof (struct iphdr ) + icmp_param.replyopts.optlen; + room -=sizeof (struct iphdr ) + icmp_param.replyopts.opt.opt.optlen; room -=sizeof (struct icmphdr ); icmp_param.data_len = skb_in->len - icmp_param.offset;
net/ipv4/inet_connection_sock.c +4 -4
@@ -356,11 +356,11 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, {struct rtable *rt;const struct inet_request_sock *ireq =inet_rsk (req); -struct ip_options *opt =inet_rsk (req)->opt; +struct ip_options_rcu *opt =inet_rsk (req)->opt;struct flowi fl = { .oif = sk->sk_bound_dev_if, .nl_u = { .ip4_u = - { .daddr = ((opt && opt->srr) ? - opt->faddr : + { .daddr = ((opt && opt->opt.srr) ? + opt->opt.faddr : ireq->rmt_addr), .saddr = ireq->loc_addr, .tos =RT_CONN_FLAGS (sk) } },
@@ -374,7 +374,7 @@ struct dst_entry *inet_csk_route_req(struct sock *sk,security_req_classify_flow (req, &fl);if (ip_route_output_flow (net, &rt, &fl, sk,0 )) goto no_route; -if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) +if (opt && opt->opt.is_strictroute && rt->rt_dst != rt->rt_gateway) goto route_err;return &rt->u.dst;
net/ipv4/ip_options.c +18 -20
@@ -35,7 +35,7 @@* saddr is address of outgoing interface. */ -void ip_options_build (struct sk_buff * skb,struct ip_options * opt, +void ip_options_build (struct sk_buff *skb,struct ip_options *opt,__be32 daddr,struct rtable *rt,int is_frag) {unsigned char *iph =skb_network_header (skb);
@@ -82,9 +82,9 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt,* NOTE: dopt cannot point to skb. */ -int ip_options_echo (struct ip_options * dopt,struct sk_buff * skb) +int ip_options_echo (struct ip_options *dopt,struct sk_buff *skb) { -struct ip_options *sopt; +const struct ip_options *sopt;unsigned char *sptr, *dptr;int soffset, doffset;int optlen;
@@ -94,10 +94,8 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) sopt = &(IPCB (skb)->opt); -if (sopt->optlen ==0 ) { - dopt->optlen =0 ; +if (sopt->optlen ==0 )return 0 ; - } sptr =skb_network_header (skb); dptr = dopt->__data;
@@ -156,7 +154,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) dopt->optlen += optlen; }if (sopt->srr) { -unsigned char * start = sptr+sopt->srr; +unsigned char *start = sptr+sopt->srr;__be32 faddr; optlen = start[1 ];
@@ -499,19 +497,19 @@ void ip_options_undo(struct ip_options * opt) } } -static struct ip_options *ip_options_get_alloc (const int optlen) +static struct ip_options_rcu *ip_options_get_alloc (const int optlen) { -return kzalloc (sizeof (struct ip_options ) + ((optlen +3 ) & ~3 ), +return kzalloc (sizeof (struct ip_options_rcu ) + ((optlen +3 ) & ~3 ),GFP_KERNEL ); } -static int ip_options_get_finish (struct net *net,struct ip_options **optp, -struct ip_options *opt,int optlen) +static int ip_options_get_finish (struct net *net,struct ip_options_rcu **optp, +struct ip_options_rcu *opt,int optlen) {while (optlen &3 ) - opt->__data[optlen++] =IPOPT_END ; - opt->optlen = optlen; -if (optlen &&ip_options_compile (net, opt,NULL )) { + opt->opt.__data[optlen++] =IPOPT_END ; + opt->opt.optlen = optlen; +if (optlen &&ip_options_compile (net, &opt->opt,NULL )) {kfree (opt);return -EINVAL ; }
@@ -520,29 +518,29 @@ static int ip_options_get_finish(struct net *net, struct ip_options **optp,return 0 ; } -int ip_options_get_from_user (struct net *net,struct ip_options **optp, +int ip_options_get_from_user (struct net *net,struct ip_options_rcu **optp,unsigned char __user *data,int optlen) { -struct ip_options *opt =ip_options_get_alloc (optlen); +struct ip_options_rcu *opt =ip_options_get_alloc (optlen);if (!opt)return -ENOMEM ; -if (optlen &©_from_user (opt->__data, data, optlen)) { +if (optlen &©_from_user (opt->opt.__data, data, optlen)) {kfree (opt);return -EFAULT ; }return ip_options_get_finish (net, optp, opt, optlen); } -int ip_options_get (struct net *net,struct ip_options **optp, +int ip_options_get (struct net *net,struct ip_options_rcu **optp,unsigned char *data,int optlen) { -struct ip_options *opt =ip_options_get_alloc (optlen); +struct ip_options_rcu *opt =ip_options_get_alloc (optlen);if (!opt)return -ENOMEM ;if (optlen) -memcpy (opt->__data, data, optlen); +memcpy (opt->opt.__data, data, optlen);return ip_options_get_finish (net, optp, opt, optlen); }
net/ipv4/ip_output.c +26 -24
134 lines not on this page · raw message
net/ipv4/ip_sockglue.c +22 -11
@@ -434,6 +434,11 @@ out: } +static void opt_kfree_rcu (struct rcu_head *head) +{ +kfree (container_of(head ,struct ip_options_rcu ,rcu )); +} +/* * Socket option code for IP. This is the end of the line after any * TCP,UDP etc options on an IP socket.
@@ -479,13 +484,15 @@ static int do_ip_setsockopt(struct sock *sk, int level,switch (optname) {case IP_OPTIONS : { -struct ip_options *opt =NULL ; +struct ip_options_rcu *old, *opt =NULL ; +if (optlen >40 || optlen <0 ) goto e_inval; err =ip_options_get_from_user (sock_net (sk), &opt, optval, optlen);if (err)break ; + old = inet->inet_opt;if (inet->is_icsk) {struct inet_connection_sock *icsk =inet_csk (sk);#if defined(CONFIG_IPV6 ) || defined(CONFIG_IPV6_MODULE )
@@ -494,17 +501,18 @@ static int do_ip_setsockopt(struct sock *sk, int level, (TCPF_LISTEN | TCPF_CLOSE)) && inet->daddr != LOOPBACK4_IPV6)) {#endif -if (inet->opt) - icsk->icsk_ext_hdr_len -= inet->opt->optlen; +if (old) + icsk->icsk_ext_hdr_len -= old->opt.optlen;if (opt) - icsk->icsk_ext_hdr_len += opt->optlen; + icsk->icsk_ext_hdr_len += opt->opt.optlen; icsk->icsk_sync_mss (sk, icsk->icsk_pmtu_cookie);#if defined(CONFIG_IPV6 ) || defined(CONFIG_IPV6_MODULE ) }#endif } - opt =xchg (&inet->opt, opt); -kfree (opt); +rcu_assign_pointer (inet->inet_opt, opt); +if (old) +call_rcu (&old->rcu, opt_kfree_rcu);break ; }case IP_PKTINFO :
@@ -1032,12 +1040,15 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,case IP_OPTIONS : {unsigned char optbuf[sizeof (struct ip_options )+40 ]; -struct ip_options * opt = (struct ip_options *)optbuf; +struct ip_options *opt = (struct ip_options *)optbuf; +struct ip_options_rcu *inet_opt; + + inet_opt = inet->inet_opt; opt->optlen =0 ; -if (inet->opt) -memcpy (optbuf, inet->opt, -sizeof (struct ip_options )+ - inet->opt->optlen); +if (inet_opt) +memcpy (optbuf, &inet_opt->opt, +sizeof (struct ip_options ) + + inet_opt->opt.optlen);release_sock (sk);if (opt->optlen ==0 )
net/ipv4/raw.c +15 -4
@@ -459,6 +459,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,__be32 saddr;u8 tos;int err; +struct ip_options_data opt_copy; err = -EMSGSIZE ;if (len >0xFFFF )
@@ -519,8 +520,18 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, saddr = ipc.addr; ipc.addr = daddr; -if (!ipc.opt) - ipc.opt = inet->opt; +if (!ipc.opt) { +struct ip_options_rcu *inet_opt; + +rcu_read_lock (); + inet_opt =rcu_dereference (inet->inet_opt); +if (inet_opt) { +memcpy (&opt_copy, inet_opt, +sizeof (*inet_opt) + inet_opt->opt.optlen); + ipc.opt = &opt_copy.opt; + } +rcu_read_unlock (); + }if (ipc.opt) { err = -EINVAL ;
@@ -529,10 +540,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,*/ if (inet->hdrincl) goto done; -if (ipc.opt->srr) { +if (ipc.opt->opt.srr) {if (!daddr) goto done; - daddr = ipc.opt->faddr; + daddr = ipc.opt->opt.faddr; } } tos =RT_CONN_FLAGS (sk);
net/ipv4/syncookies.c +2 -2
@@ -309,10 +309,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,* the ACK carries the same options again (see RFC1122 4.2.3.8) */ if (opt && opt->optlen) { -int opt_size =sizeof (struct ip_options ) + opt->optlen; +int opt_size =sizeof (struct ip_options_rcu ) + opt->optlen; ireq->opt =kmalloc (opt_size,GFP_ATOMIC ); -if (ireq->opt !=NULL &&ip_options_echo (ireq->opt, skb)) { +if (ireq->opt !=NULL &&ip_options_echo (&ireq->opt->opt, skb)) {kfree (ireq->opt); ireq->opt =NULL ; }
net/ipv4/tcp_ipv4.c +19 -14
@@ -152,6 +152,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)__be32 daddr, nexthop;int tmp;int err; +struct ip_options_rcu *inet_opt;if (addr_len <sizeof (struct sockaddr_in ))return -EINVAL ;
@@ -160,10 +161,11 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)return -EAFNOSUPPORT ; nexthop = daddr = usin->sin_addr.s_addr; -if (inet->opt && inet->opt->srr) { + inet_opt = inet->inet_opt; +if (inet_opt && inet_opt->opt.srr) {if (!daddr)return -EINVAL ; - nexthop = inet->opt->faddr; + nexthop = inet_opt->opt.faddr; } tmp = ip_route_connect(&rt, nexthop, inet->saddr,
@@ -181,7 +183,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)return -ENETUNREACH ; } -if (!inet->opt || !inet->opt->srr) +if (!inet_opt || !inet_opt->opt.srr) daddr = rt->rt_dst;if (!inet->saddr)
@@ -215,8 +217,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->daddr = daddr;inet_csk (sk)->icsk_ext_hdr_len =0 ; -if (inet->opt) -inet_csk (sk)->icsk_ext_hdr_len = inet->opt->optlen; +if (inet_opt) +inet_csk (sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; tp->rx_opt.mss_clamp =536 ;
@@ -802,17 +804,18 @@ static void syn_flood_warning(struct sk_buff *skb)/* * Save and compile IPv4 options into the request_sock if needed. */ -static struct ip_options *tcp_v4_save_options (struct sock *sk, -struct sk_buff *skb) +static struct ip_options_rcu *tcp_v4_save_options (struct sock *sk, +struct sk_buff *skb) { -struct ip_options *opt = &(IPCB (skb)->opt); -struct ip_options *dopt =NULL ; +const struct ip_options *opt = &(IPCB (skb)->opt); +struct ip_options_rcu *dopt =NULL ;if (opt && opt->optlen) { -int opt_size =optlength (opt); +int opt_size =sizeof (*dopt) + opt->optlen; + dopt =kmalloc (opt_size,GFP_ATOMIC );if (dopt) { -if (ip_options_echo (dopt, skb)) { +if (ip_options_echo (&dopt->opt, skb)) {kfree (dopt); dopt =NULL ; }
@@ -1362,6 +1365,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,#ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key;#endif +struct ip_options_rcu *inet_opt;if (sk_acceptq_is_full (sk)) goto exit_overflow;
@@ -1382,13 +1386,14 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newinet->daddr = ireq->rmt_addr; newinet->rcv_saddr = ireq->loc_addr; newinet->saddr = ireq->loc_addr; - newinet->opt = ireq->opt; + inet_opt = ireq->opt; +rcu_assign_pointer (newinet->inet_opt, inet_opt); ireq->opt =NULL ; newinet->mc_index =inet_iif (skb); newinet->mc_ttl =ip_hdr (skb)->ttl;inet_csk (newsk)->icsk_ext_hdr_len =0 ; -if (newinet->opt) -inet_csk (newsk)->icsk_ext_hdr_len = newinet->opt->optlen; +if (inet_opt) +inet_csk (newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen; newinet->id = newtp->write_seq ^ jiffies;tcp_mtup_init (newsk);
net/ipv4/udp.c +16 -5
@@ -592,6 +592,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,int err, is_udplite =IS_UDPLITE (sk);int corkreq = up->corkflag || msg->msg_flags&MSG_MORE ;int (*getfrag)(void *,char *,int ,int ,int ,struct sk_buff *); +struct ip_options_data opt_copy;if (len >0xFFFF )return -EMSGSIZE ;
@@ -663,22 +664,32 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, free =1 ; connected =0 ; } -if (!ipc.opt) - ipc.opt = inet->opt; +if (!ipc.opt) { +struct ip_options_rcu *inet_opt; + +rcu_read_lock (); + inet_opt =rcu_dereference (inet->inet_opt); +if (inet_opt) { +memcpy (&opt_copy, inet_opt, +sizeof (*inet_opt) + inet_opt->opt.optlen); + ipc.opt = &opt_copy.opt; + } +rcu_read_unlock (); + } saddr = ipc.addr; ipc.addr = faddr = daddr; -if (ipc.opt && ipc.opt->srr) { +if (ipc.opt && ipc.opt->opt.srr) {if (!daddr)return -EINVAL ; - faddr = ipc.opt->faddr; + faddr = ipc.opt->opt.faddr; connected =0 ; } tos =RT_TOS (inet->tos);if (sock_flag (sk,SOCK_LOCALROUTE ) || (msg->msg_flags &MSG_DONTROUTE ) || - (ipc.opt && ipc.opt->is_strictroute)) { + (ipc.opt && ipc.opt->opt.is_strictroute)) { tos |=RTO_ONLINK ; connected =0 ; }
net/ipv6/tcp_ipv6.c +1 -1
@@ -1391,7 +1391,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,First: no IPv4 options. */ - newinet->opt =NULL ; + newinet->inet_opt =NULL ; newnp->ipv6_fl_list =NULL ;/* Clone RX bits */
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
Re: [ 157/184] inet: add RCU protection to inet->opt
Quoted 21 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit
f6d8bd051c391c1c0458a30b2a7abcd939329259upstream.We lack proper synchronization to manipulate
inet->optip_optionsProblem is
ip_make_skb()callsip_setup_cork()andip_setup_cork()possibly makes a copy ofipc->opt(structip_options), without any protection against another thread manipulatinginet->opt.Another thread can change
inet->optpointer and free old one under us.Use RCU to protect
inet->opt(changed toinet->inet_opt).Instead of handling atomic refcounts, just copy
ip_optionswhen necessary, to avoid cache line dirtying.
Quoted 8 lines
We cant insert an
rcu_headin structip_optionssince its included in skb->cb[], so this patch is large because I had to introduce a newip_options_rcustructure.Signed-off-by Eric Dumazet <[email protected]>Cc Herbert Xu <[email protected]>Signed-off-by David S. Miller <[email protected]>[dannf/bwh: backported to Debian's 2.6.32]
Quoted 1,109 lines
Signed-off-by Willy Tarreau <[email protected]>--- include/net/inet_sock.h | 14 +++-- include/net/ip.h | 11 ++-- net/dccp/ipv4.c | 15 +++--- net/dccp/ipv6.c | 2 +- net/ipv4/af_inet.c | 16 ++++-- net/ipv4/cipso_ipv4.c | 113 ++++++++++++++++++++++------------------ net/ipv4/icmp.c | 23 ++++---- net/ipv4/inet_connection_sock.c | 8 +-- net/ipv4/ip_options.c | 38 +++++++------- net/ipv4/ip_output.c | 50 +++++++++--------- net/ipv4/ip_sockglue.c | 33 ++++++++---- net/ipv4/raw.c | 19 +++++-- net/ipv4/syncookies.c | 4 +- net/ipv4/tcp_ipv4.c | 33 +++++++----- net/ipv4/udp.c | 21 ++++++-- net/ipv6/tcp_ipv6.c | 2 +- 16 files changed, 235 insertions(+), 167 deletions(-)include/net/inet_sock.h +11
-3@@ -56,7 +56,15 @@ struct ip_options {unsigned char __data[0 ]; }; -#define optlength (opt) (sizeof(struct ip_options) + opt->optlen) +struct ip_options_rcu { +struct rcu_head rcu; +struct ip_options opt; +}; + +struct ip_options_data { +struct ip_options_rcu opt; +char data[40 ]; +};struct inet_request_sock {struct request_sock req;@@ -77,7 +85,7 @@ struct inet_request_sock { acked :1 , no_srccheck: 1;kmemcheck_bitfield_end (flags); -struct ip_options *opt; +struct ip_options_rcu *opt; };static inline struct inet_request_sock *inet_rsk (const struct request_sock *sk)@@ -122,7 +130,7 @@ struct inet_sock {__be32 saddr;__s16 uc_ttl;__u16 cmsg_flags; -struct ip_options *opt; +struct ip_options_rcu *inet_opt;__be16 sport;__u16 id;__u8 tos;include/net/ip.h +6
-5@@ -54,7 +54,7 @@ struct ipcm_cookie {__be32 addr;int oif; -struct ip_options *opt; +struct ip_options_rcu *opt;union skb_shared_tx shtx; };@@ -92,7 +92,7 @@ extern int igmp_mc_proc_init(void);extern int ip_build_and_send_pkt (struct sk_buff *skb,struct sock *sk,__be32 saddr,__be32 daddr, -struct ip_options *opt); +struct ip_options_rcu *opt);extern int ip_rcv (struct sk_buff *skb,struct net_device *dev,struct packet_type *pt,struct net_device *orig_dev);extern int ip_local_deliver (struct sk_buff *skb);@@ -362,14 +362,15 @@ extern int ip_forward(struct sk_buff *skb);* Functions provided by ip_options.c */ -extern void ip_options_build (struct sk_buff *skb,struct ip_options *opt,__be32 daddr,struct rtable *rt,int is_frag); +extern void ip_options_build (struct sk_buff *skb,struct ip_options *opt, +__be32 daddr,struct rtable *rt,int is_frag);extern int ip_options_echo (struct ip_options *dopt,struct sk_buff *skb);extern void ip_options_fragment (struct sk_buff *skb);extern int ip_options_compile (struct net *net,struct ip_options *opt,struct sk_buff *skb); -extern int ip_options_get (struct net *net,struct ip_options **optp, +extern int ip_options_get (struct net *net,struct ip_options_rcu **optp,unsigned char *data,int optlen); -extern int ip_options_get_from_user (struct net *net,struct ip_options **optp, +extern int ip_options_get_from_user (struct net *net,struct ip_options_rcu **optp,unsigned char __user *data,int optlen);extern void ip_options_undo (struct ip_options * opt);extern void ip_forward_options (struct sk_buff *skb);net/dccp/ipv4.c +9
-6@@ -47,6 +47,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)__be32 daddr, nexthop;int tmp;int err; +struct ip_options_rcu *inet_opt; dp->dccps_role =DCCP_ROLE_CLIENT ;@@ -57,10 +58,12 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)return -EAFNOSUPPORT ; nexthop = daddr = usin->sin_addr.s_addr; -if (inet->opt !=NULL && inet->opt->srr) { + + inet_opt = inet->inet_opt; +if (inet_opt !=NULL && inet_opt->opt.srr) {if (daddr ==0 )return -EINVAL ; - nexthop = inet->opt->faddr; + nexthop = inet_opt->opt.faddr; } tmp = ip_route_connect(&rt, nexthop, inet->saddr,@@ -75,7 +78,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)return -ENETUNREACH ; } -if (inet->opt ==NULL || !inet->opt->srr) +if (inet_opt ==NULL || !inet_opt->opt.srr) daddr = rt->rt_dst;if (inet->saddr ==0 )@@ -86,8 +89,8 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->daddr = daddr;inet_csk (sk)->icsk_ext_hdr_len =0 ; -if (inet->opt !=NULL ) -inet_csk (sk)->icsk_ext_hdr_len = inet->opt->optlen; +if (inet_opt) +inet_csk (sk)->icsk_ext_hdr_len = inet_opt->opt.optlen;/* * Socket identity is still unknown (sport may be zero). * However we set state to DCCP_REQUESTING and not releasing socket @@ -397,7 +400,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, newinet->daddr = ireq->rmt_addr; newinet->rcv_saddr = ireq->loc_addr; newinet->saddr = ireq->loc_addr; - newinet->opt = ireq->opt; + newinet->inet_opt = ireq->opt; ireq->opt =NULL ; newinet->mc_index =inet_iif (skb); newinet->mc_ttl =ip_hdr (skb)->ttl;net/dccp/ipv6.c +1
-1@@ -600,7 +600,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,First: no IPv4 options. */ - newinet->opt =NULL ; + newinet->inet_opt =NULL ;/* Clone RX bits */ newnp->rxopt.all = np->rxopt.all;net/ipv4/af_inet.c +11
-5@@ -152,7 +152,7 @@ void inet_sock_destruct(struct sock *sk)WARN_ON (sk->sk_wmem_queued);WARN_ON (sk->sk_forward_alloc); -kfree (inet->opt); +kfree (inet->inet_opt);dst_release (sk->sk_dst_cache);sk_refcnt_debug_dec (sk); }@@ -1065,9 +1065,11 @@ static int inet_sk_reselect_saddr(struct sock *sk)__be32 old_saddr = inet->saddr;__be32 new_saddr;__be32 daddr = inet->daddr; +struct ip_options_rcu *inet_opt; -if (inet->opt && inet->opt->srr) - daddr = inet->opt->faddr; + inet_opt = inet->inet_opt; +if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr;/* Query new route. */ err = ip_route_connect(&rt, daddr, 0,@@ -1109,6 +1111,7 @@ int inet_sk_rebuild_header(struct sock *sk)struct inet_sock *inet =inet_sk (sk);struct rtable *rt = (struct rtable *)__sk_dst_check (sk,0 );__be32 daddr; +struct ip_options_rcu *inet_opt;int err;/* Route is OK, nothing to do. */ @@ -1116,9 +1119,12 @@ int inet_sk_rebuild_header(struct sock *sk)return 0 ;/* Reroute. */ +rcu_read_lock (); + inet_opt =rcu_dereference (inet->inet_opt); daddr = inet->daddr; -if (inet->opt && inet->opt->srr) - daddr = inet->opt->faddr; +if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr; +rcu_read_unlock (); {struct flowi fl = { .oif = sk->sk_bound_dev_if,net/ipv4/cipso_ipv4.c +63
-50@@ -1860,6 +1860,11 @@ static int cipso_v4_genopt(unsigned char *buf, u32 buf_len,return CIPSO_V4_HDR_LEN + ret_val; } +static void opt_kfree_rcu (struct rcu_head *head) +{ +kfree (container_of(head ,struct ip_options_rcu ,rcu )); +} +/** * cipso_v4_sock_setattr - Add a CIPSO option to a socket * @sk: the socket @@ -1882,7 +1887,7 @@ int cipso_v4_sock_setattr(struct sock *sk,unsigned char *buf =NULL ;u32 buf_len;u32 opt_len; -struct ip_options *opt =NULL ; +struct ip_options_rcu *old, *opt =NULL ;struct inet_sock *sk_inet;struct inet_connection_sock *sk_conn;@@ -1918,22 +1923,25 @@ int cipso_v4_sock_setattr(struct sock *sk, ret_val = -ENOMEM ; goto socket_setattr_failure; } -memcpy (opt->__data, buf, buf_len); - opt->optlen = opt_len; - opt->cipso =sizeof (struct iphdr ); +memcpy (opt->opt.__data, buf, buf_len); + opt->opt.optlen = opt_len; + opt->opt.cipso =sizeof (struct iphdr );kfree (buf); buf =NULL ; sk_inet =inet_sk (sk); + + old = sk_inet->inet_opt;if (sk_inet->is_icsk) { sk_conn =inet_csk (sk); -if (sk_inet->opt) - sk_conn->icsk_ext_hdr_len -= sk_inet->opt->optlen; - sk_conn->icsk_ext_hdr_len += opt->optlen; +if (old) + sk_conn->icsk_ext_hdr_len -= old->opt.optlen; + sk_conn->icsk_ext_hdr_len += opt->opt.optlen; sk_conn->icsk_sync_mss (sk, sk_conn->icsk_pmtu_cookie); } - opt =xchg (&sk_inet->opt, opt); -kfree (opt); +rcu_assign_pointer (sk_inet->inet_opt, opt); +if (old) +call_rcu (&old->rcu, opt_kfree_rcu);return 0 ;@@ -1963,7 +1971,7 @@ int cipso_v4_req_setattr(struct request_sock *req,unsigned char *buf =NULL ;u32 buf_len;u32 opt_len; -struct ip_options *opt =NULL ; +struct ip_options_rcu *opt =NULL ;struct inet_request_sock *req_inet;/* We allocate the maximum CIPSO option size here so we are probably @@ -1991,15 +1999,16 @@ int cipso_v4_req_setattr(struct request_sock *req, ret_val = -ENOMEM ; goto req_setattr_failure; } -memcpy (opt->__data, buf, buf_len); - opt->optlen = opt_len; - opt->cipso =sizeof (struct iphdr ); +memcpy (opt->opt.__data, buf, buf_len); + opt->opt.optlen = opt_len; + opt->opt.cipso =sizeof (struct iphdr );kfree (buf); buf =NULL ; req_inet =inet_rsk (req); opt =xchg (&req_inet->opt, opt); -kfree (opt); +if (opt) +call_rcu (&opt->rcu, opt_kfree_rcu);return 0 ;@@ -2019,34 +2028,34 @@ req_setattr_failure:* values on failure. * */ -int cipso_v4_delopt (struct ip_options **opt_ptr) +int cipso_v4_delopt (struct ip_options_rcu **opt_ptr) {int hdr_delta =0 ; -struct ip_options *opt = *opt_ptr; +struct ip_options_rcu *opt = *opt_ptr; -if (opt->srr || opt->rr || opt->ts || opt->router_alert) { +if (opt->opt.srr || opt->opt.rr || opt->opt.ts || opt->opt.router_alert) {u8 cipso_len;u8 cipso_off;unsigned char *cipso_ptr;int iter;int optlen_new; - cipso_off = opt->cipso -sizeof (struct iphdr ); - cipso_ptr = &opt->__data[cipso_off]; + cipso_off = opt->opt.cipso -sizeof (struct iphdr ); + cipso_ptr = &opt->opt.__data[cipso_off]; cipso_len = cipso_ptr[1 ]; -if (opt->srr > opt->cipso) - opt->srr -= cipso_len; -if (opt->rr > opt->cipso) - opt->rr -= cipso_len; -if (opt->ts > opt->cipso) - opt->ts -= cipso_len; -if (opt->router_alert > opt->cipso) - opt->router_alert -= cipso_len; - opt->cipso =0 ; +if (opt->opt.srr > opt->opt.cipso) + opt->opt.srr -= cipso_len; +if (opt->opt.rr > opt->opt.cipso) + opt->opt.rr -= cipso_len; +if (opt->opt.ts > opt->opt.cipso) + opt->opt.ts -= cipso_len; +if (opt->opt.router_alert > opt->opt.cipso) + opt->opt.router_alert -= cipso_len; + opt->opt.cipso =0 ;memmove (cipso_ptr, cipso_ptr + cipso_len, - opt->optlen - cipso_off - cipso_len); + opt->opt.optlen - cipso_off - cipso_len);/* determining the new total option length is tricky because of * the padding necessary, the only thing i can think to do at @@ -2055,21 +2064,21 @@ int cipso_v4_delopt(struct ip_options **opt_ptr)* from there we can determine the new total option length */ iter =0 ; optlen_new =0 ; -while (iter < opt->optlen) -if (opt->__data[iter] !=IPOPT_NOP ) { - iter += opt->__data[iter +1 ]; +while (iter < opt->opt.optlen) +if (opt->opt.__data[iter] !=IPOPT_NOP ) { + iter += opt->opt.__data[iter +1 ]; optlen_new = iter; }else iter++; - hdr_delta = opt->optlen; - opt->optlen = (optlen_new +3 ) & ~3 ; - hdr_delta -= opt->optlen; + hdr_delta = opt->opt.optlen; + opt->opt.optlen = (optlen_new +3 ) & ~3 ; + hdr_delta -= opt->opt.optlen; }else {/* only the cipso option was present on the socket so we can * remove the entire option struct */ *opt_ptr =NULL ; - hdr_delta = opt->optlen; -kfree (opt); + hdr_delta = opt->opt.optlen; +call_rcu (&opt->rcu, opt_kfree_rcu); }return hdr_delta;@@ -2086,15 +2095,15 @@ int cipso_v4_delopt(struct ip_options **opt_ptr)void cipso_v4_sock_delattr (struct sock *sk) {int hdr_delta; -struct ip_options *opt; +struct ip_options_rcu *opt;struct inet_sock *sk_inet; sk_inet =inet_sk (sk); - opt = sk_inet->opt; -if (opt ==NULL || opt->cipso ==0 ) + opt = sk_inet->inet_opt; +if (opt ==NULL || opt->opt.cipso ==0 )return ; - hdr_delta =cipso_v4_delopt (&sk_inet->opt); + hdr_delta =cipso_v4_delopt (&sk_inet->inet_opt);if (sk_inet->is_icsk && hdr_delta >0 ) {struct inet_connection_sock *sk_conn =inet_csk (sk); sk_conn->icsk_ext_hdr_len -= hdr_delta;@@ -2112,12 +2121,12 @@ void cipso_v4_sock_delattr(struct sock *sk)*/ void cipso_v4_req_delattr (struct request_sock *req) { -struct ip_options *opt; +struct ip_options_rcu *opt;struct inet_request_sock *req_inet; req_inet =inet_rsk (req); opt = req_inet->opt; -if (opt ==NULL || opt->cipso ==0 ) +if (opt ==NULL || opt->opt.cipso ==0 )return ;cipso_v4_delopt (&req_inet->opt);@@ -2187,14 +2196,18 @@ getattr_return:*/ int cipso_v4_sock_getattr (struct sock *sk,struct netlbl_lsm_secattr *secattr) { -struct ip_options *opt; +struct ip_options_rcu *opt; +int res = -ENOMSG ; - opt =inet_sk (sk)->opt; -if (opt ==NULL || opt->cipso ==0 ) -return -ENOMSG ; - -return cipso_v4_getattr (opt->__data + opt->cipso -sizeof (struct iphdr ), - secattr); +rcu_read_lock (); + opt =rcu_dereference (inet_sk (sk)->inet_opt); +if (opt && opt->opt.cipso) + res =cipso_v4_getattr (opt->opt.__data + + opt->opt.cipso - +sizeof (struct iphdr ), + secattr); +rcu_read_unlock (); +return res; }/** net/ipv4/icmp.c +11
-12@@ -107,8 +107,7 @@ struct icmp_bxm {__be32 times[3 ]; } data;int head_len; -struct ip_options replyopts; -unsigned char optbuf[40 ]; +struct ip_options_data replyopts; };/* An array of errno for error messages from dest unreach. */ @@ -362,7 +361,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)struct inet_sock *inet;__be32 daddr; -if (ip_options_echo (&icmp_param->replyopts, skb)) +if (ip_options_echo (&icmp_param->replyopts.opt.opt, skb))return ; sk =icmp_xmit_lock (net);@@ -376,10 +375,10 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) daddr = ipc.addr = rt->rt_src; ipc.opt =NULL ; ipc.shtx.flags =0 ; -if (icmp_param->replyopts.optlen) { - ipc.opt = &icmp_param->replyopts; -if (ipc.opt->srr) - daddr = icmp_param->replyopts.faddr; +if (icmp_param->replyopts.opt.opt.optlen) { + ipc.opt = &icmp_param->replyopts.opt; +if (ipc.opt->opt.srr) + daddr = icmp_param->replyopts.opt.opt.faddr; } { struct flowi fl = { .nl_u = { .ip4_u =@@ -516,7 +515,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) IPTOS_PREC_INTERNETCONTROL) : iph->tos; -if (ip_options_echo (&icmp_param.replyopts, skb_in)) +if (ip_options_echo (&icmp_param.replyopts.opt.opt, skb_in)) goto out_unlock;@@ -532,15 +531,15 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) icmp_param.offset =skb_network_offset (skb_in);inet_sk (sk)->tos = tos; ipc.addr = iph->saddr; - ipc.opt = &icmp_param.replyopts; + ipc.opt = &icmp_param.replyopts.opt; ipc.shtx.flags =0 ; {struct flowi fl = { .nl_u = { .ip4_u = { - .daddr = icmp_param.replyopts.srr ? - icmp_param.replyopts.faddr : + .daddr = icmp_param.replyopts.opt.opt.srr ? + icmp_param.replyopts.opt.opt.faddr : iph->saddr, .saddr = saddr, .tos =RT_TOS (tos)@@ -629,7 +628,7 @@ route_done: room =dst_mtu (&rt->u.dst);if (room >576 ) room =576 ; - room -=sizeof (struct iphdr ) + icmp_param.replyopts.optlen; + room -=sizeof (struct iphdr ) + icmp_param.replyopts.opt.opt.optlen; room -=sizeof (struct icmphdr ); icmp_param.data_len = skb_in->len - icmp_param.offset;net/ipv4/inet_connection_sock.c +4
-4@@ -356,11 +356,11 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, {struct rtable *rt;const struct inet_request_sock *ireq =inet_rsk (req); -struct ip_options *opt =inet_rsk (req)->opt; +struct ip_options_rcu *opt =inet_rsk (req)->opt;struct flowi fl = { .oif = sk->sk_bound_dev_if, .nl_u = { .ip4_u = - { .daddr = ((opt && opt->srr) ? - opt->faddr : + { .daddr = ((opt && opt->opt.srr) ? + opt->opt.faddr : ireq->rmt_addr), .saddr = ireq->loc_addr, .tos =RT_CONN_FLAGS (sk) } },@@ -374,7 +374,7 @@ struct dst_entry *inet_csk_route_req(struct sock *sk,security_req_classify_flow (req, &fl);if (ip_route_output_flow (net, &rt, &fl, sk,0 )) goto no_route; -if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) +if (opt && opt->opt.is_strictroute && rt->rt_dst != rt->rt_gateway) goto route_err;return &rt->u.dst;net/ipv4/ip_options.c +18
-20@@ -35,7 +35,7 @@* saddr is address of outgoing interface. */ -void ip_options_build (struct sk_buff * skb,struct ip_options * opt, +void ip_options_build (struct sk_buff *skb,struct ip_options *opt,__be32 daddr,struct rtable *rt,int is_frag) {unsigned char *iph =skb_network_header (skb);@@ -82,9 +82,9 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt,* NOTE: dopt cannot point to skb. */ -int ip_options_echo (struct ip_options * dopt,struct sk_buff * skb) +int ip_options_echo (struct ip_options *dopt,struct sk_buff *skb) { -struct ip_options *sopt; +const struct ip_options *sopt;unsigned char *sptr, *dptr;int soffset, doffset;int optlen;@@ -94,10 +94,8 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) sopt = &(IPCB (skb)->opt); -if (sopt->optlen ==0 ) { - dopt->optlen =0 ; +if (sopt->optlen ==0 )return 0 ; - } sptr =skb_network_header (skb); dptr = dopt->__data;@@ -156,7 +154,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) dopt->optlen += optlen; }if (sopt->srr) { -unsigned char * start = sptr+sopt->srr; +unsigned char *start = sptr+sopt->srr;__be32 faddr; optlen = start[1 ];@@ -499,19 +497,19 @@ void ip_options_undo(struct ip_options * opt) } } -static struct ip_options *ip_options_get_alloc (const int optlen) +static struct ip_options_rcu *ip_options_get_alloc (const int optlen) { -return kzalloc (sizeof (struct ip_options ) + ((optlen +3 ) & ~3 ), +return kzalloc (sizeof (struct ip_options_rcu ) + ((optlen +3 ) & ~3 ),GFP_KERNEL ); } -static int ip_options_get_finish (struct net *net,struct ip_options **optp, -struct ip_options *opt,int optlen) +static int ip_options_get_finish (struct net *net,struct ip_options_rcu **optp, +struct ip_options_rcu *opt,int optlen) {while (optlen &3 ) - opt->__data[optlen++] =IPOPT_END ; - opt->optlen = optlen; -if (optlen &&ip_options_compile (net, opt,NULL )) { + opt->opt.__data[optlen++] =IPOPT_END ; + opt->opt.optlen = optlen; +if (optlen &&ip_options_compile (net, &opt->opt,NULL )) {kfree (opt);return -EINVAL ; }@@ -520,29 +518,29 @@ static int ip_options_get_finish(struct net *net, struct ip_options **optp,return 0 ; } -int ip_options_get_from_user (struct net *net,struct ip_options **optp, +int ip_options_get_from_user (struct net *net,struct ip_options_rcu **optp,unsigned char __user *data,int optlen) { -struct ip_options *opt =ip_options_get_alloc (optlen); +struct ip_options_rcu *opt =ip_options_get_alloc (optlen);if (!opt)return -ENOMEM ; -if (optlen &©_from_user (opt->__data, data, optlen)) { +if (optlen &©_from_user (opt->opt.__data, data, optlen)) {kfree (opt);return -EFAULT ; }return ip_options_get_finish (net, optp, opt, optlen); } -int ip_options_get (struct net *net,struct ip_options **optp, +int ip_options_get (struct net *net,struct ip_options_rcu **optp,unsigned char *data,int optlen) { -struct ip_options *opt =ip_options_get_alloc (optlen); +struct ip_options_rcu *opt =ip_options_get_alloc (optlen);if (!opt)return -ENOMEM ;if (optlen) -memcpy (opt->__data, data, optlen); +memcpy (opt->opt.__data, data, optlen);return ip_options_get_finish (net, optp, opt, optlen); }net/ipv4/ip_output.c +26
-24@@ -137,14 +137,14 @@ static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst)* */ int ip_build_and_send_pkt (struct sk_buff *skb,struct sock *sk, -__be32 saddr,__be32 daddr,struct ip_options *opt) +__be32 saddr,__be32 daddr,struct ip_options_rcu *opt) {struct inet_sock *inet =inet_sk (sk);struct rtable *rt =skb_rtable (skb);struct iphdr *iph;/* Build the IP header. */ -skb_push (skb,sizeof (struct iphdr ) + (opt ? opt->optlen :0 )); +skb_push (skb,sizeof (struct iphdr ) + (opt ? opt->opt.optlen :0 ));skb_reset_network_header (skb); iph =ip_hdr (skb); iph->version =4 ;@@ -160,9 +160,9 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, iph->protocol = sk->sk_protocol;ip_select_ident (iph, &rt->u.dst, sk); -if (opt && opt->optlen) { - iph->ihl += opt->optlen>>2 ; -ip_options_build (skb, opt, daddr, rt,0 ); +if (opt && opt->opt.optlen) { + iph->ihl += opt->opt.optlen>>2 ; +ip_options_build (skb, &opt->opt, daddr, rt,0 ); } skb->priority = sk->sk_priority;@@ -312,9 +312,10 @@ int ip_queue_xmit(struct sk_buff *skb, int ipfragok) {struct sock *sk = skb->sk;struct inet_sock *inet =inet_sk (sk); -struct ip_options *opt = inet->opt; +struct ip_options_rcu *inet_opt =NULL ;struct rtable *rt;struct iphdr *iph; +int res;/* Skip all of this if the packet is already routed, * f.e. by something like SCTP. @@ -325,13 +326,15 @@ int ip_queue_xmit(struct sk_buff *skb, int ipfragok)/* Make sure we can route this packet. */ rt = (struct rtable *)__sk_dst_check (sk,0 ); +rcu_read_lock (); + inet_opt =rcu_dereference (inet->inet_opt);if (rt ==NULL ) {__be32 daddr;/* Use correct destination address if we have options. */ daddr = inet->daddr; -if (opt && opt->srr) - daddr = opt->faddr; +if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr; { struct flowi fl = { .oif = sk->sk_bound_dev_if,@@ -359,11 +362,11 @@ int ip_queue_xmit(struct sk_buff *skb, int ipfragok)skb_dst_set (skb,dst_clone (&rt->u.dst)); packet_routed: -if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) +if (inet_opt && inet_opt->opt.is_strictroute && rt->rt_dst != rt->rt_gateway) goto no_route;/* OK, we know where to send it, allocate and build IP header. */ -skb_push (skb,sizeof (struct iphdr ) + (opt ? opt->optlen :0 )); +skb_push (skb,sizeof (struct iphdr ) + (inet_opt ? inet_opt->opt.optlen :0 ));skb_reset_network_header (skb); iph =ip_hdr (skb); *((__be16 *)iph) =htons ((4 <<12 ) | (5 <<8 ) | (inet->tos &0xff ));@@ -377,9 +380,9 @@ packet_routed: iph->daddr = rt->rt_dst;/* Transport layer set skb->h.foo itself. */ -if (opt && opt->optlen) { - iph->ihl += opt->optlen >>2 ; -ip_options_build (skb, opt, inet->daddr, rt,0 ); +if (inet_opt && inet_opt->opt.optlen) { + iph->ihl += inet_opt->opt.optlen >>2 ; +ip_options_build (skb, &inet_opt->opt, inet->daddr, rt,0 ); } ip_select_ident_more(iph, &rt->u.dst, sk,@@ -387,10 +390,12 @@ packet_routed: skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; - -return ip_local_out (skb); + res =ip_local_out (skb); +rcu_read_unlock (); +return res; no_route: +rcu_read_unlock ();IP_INC_STATS (sock_net (sk),IPSTATS_MIB_OUTNOROUTES );kfree_skb (skb);return -EHOSTUNREACH ;@@ -809,7 +814,7 @@ int ip_append_data(struct sock *sk,/* * setup for corking. */ - opt = ipc->opt; + opt = ipc->opt ? &ipc->opt->opt :NULL ;if (opt) {if (inet->cork.opt ==NULL ) { inet->cork.opt =kmalloc (sizeof (struct ip_options ) +40 , sk->sk_allocation);@@ -1367,26 +1372,23 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar unsigned int len) {struct inet_sock *inet =inet_sk (sk); -struct { -struct ip_options opt; -char data[40 ]; - } replyopts; +struct ip_options_data replyopts;struct ipcm_cookie ipc;__be32 daddr;struct rtable *rt =skb_rtable (skb); -if (ip_options_echo (&replyopts.opt, skb)) +if (ip_options_echo (&replyopts.opt.opt, skb))return ; daddr = ipc.addr = rt->rt_src; ipc.opt =NULL ; ipc.shtx.flags =0 ; -if (replyopts.opt.optlen) { +if (replyopts.opt.opt.optlen) { ipc.opt = &replyopts.opt; -if (ipc.opt->srr) - daddr = replyopts.opt.faddr; +if (replyopts.opt.opt.srr) + daddr = replyopts.opt.opt.faddr; } {net/ipv4/ip_sockglue.c +22
-11@@ -434,6 +434,11 @@ out: } +static void opt_kfree_rcu (struct rcu_head *head) +{ +kfree (container_of(head ,struct ip_options_rcu ,rcu )); +} +/* * Socket option code for IP. This is the end of the line after any * TCP,UDP etc options on an IP socket. @@ -479,13 +484,15 @@ static int do_ip_setsockopt(struct sock *sk, int level,switch (optname) {case IP_OPTIONS : { -struct ip_options *opt =NULL ; +struct ip_options_rcu *old, *opt =NULL ; +if (optlen >40 || optlen <0 ) goto e_inval; err =ip_options_get_from_user (sock_net (sk), &opt, optval, optlen);if (err)break ; + old = inet->inet_opt;if (inet->is_icsk) {struct inet_connection_sock *icsk =inet_csk (sk);#if defined(CONFIG_IPV6 ) || defined(CONFIG_IPV6_MODULE )@@ -494,17 +501,18 @@ static int do_ip_setsockopt(struct sock *sk, int level, (TCPF_LISTEN | TCPF_CLOSE)) && inet->daddr != LOOPBACK4_IPV6)) {#endif -if (inet->opt) - icsk->icsk_ext_hdr_len -= inet->opt->optlen; +if (old) + icsk->icsk_ext_hdr_len -= old->opt.optlen;if (opt) - icsk->icsk_ext_hdr_len += opt->optlen; + icsk->icsk_ext_hdr_len += opt->opt.optlen; icsk->icsk_sync_mss (sk, icsk->icsk_pmtu_cookie);#if defined(CONFIG_IPV6 ) || defined(CONFIG_IPV6_MODULE ) }#endif } - opt =xchg (&inet->opt, opt); -kfree (opt); +rcu_assign_pointer (inet->inet_opt, opt); +if (old) +call_rcu (&old->rcu, opt_kfree_rcu);break ; }case IP_PKTINFO :@@ -1032,12 +1040,15 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,case IP_OPTIONS : {unsigned char optbuf[sizeof (struct ip_options )+40 ]; -struct ip_options * opt = (struct ip_options *)optbuf; +struct ip_options *opt = (struct ip_options *)optbuf; +struct ip_options_rcu *inet_opt; + + inet_opt = inet->inet_opt; opt->optlen =0 ; -if (inet->opt) -memcpy (optbuf, inet->opt, -sizeof (struct ip_options )+ - inet->opt->optlen); +if (inet_opt) +memcpy (optbuf, &inet_opt->opt, +sizeof (struct ip_options ) + + inet_opt->opt.optlen);release_sock (sk);if (opt->optlen ==0 )net/ipv4/raw.c +15
-4@@ -459,6 +459,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,__be32 saddr;u8 tos;int err; +struct ip_options_data opt_copy; err = -EMSGSIZE ;if (len >0xFFFF )@@ -519,8 +520,18 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, saddr = ipc.addr; ipc.addr = daddr; -if (!ipc.opt) - ipc.opt = inet->opt; +if (!ipc.opt) { +struct ip_options_rcu *inet_opt; + +rcu_read_lock (); + inet_opt =rcu_dereference (inet->inet_opt); +if (inet_opt) { +memcpy (&opt_copy, inet_opt, +sizeof (*inet_opt) + inet_opt->opt.optlen); + ipc.opt = &opt_copy.opt; + } +rcu_read_unlock (); + }if (ipc.opt) { err = -EINVAL ;@@ -529,10 +540,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,*/ if (inet->hdrincl) goto done; -if (ipc.opt->srr) { +if (ipc.opt->opt.srr) {if (!daddr) goto done; - daddr = ipc.opt->faddr; + daddr = ipc.opt->opt.faddr; } } tos =RT_CONN_FLAGS (sk);net/ipv4/syncookies.c +2
-2@@ -309,10 +309,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,* the ACK carries the same options again (see RFC1122 4.2.3.8) */ if (opt && opt->optlen) { -int opt_size =sizeof (struct ip_options ) + opt->optlen; +int opt_size =sizeof (struct ip_options_rcu ) + opt->optlen; ireq->opt =kmalloc (opt_size,GFP_ATOMIC ); -if (ireq->opt !=NULL &&ip_options_echo (ireq->opt, skb)) { +if (ireq->opt !=NULL &&ip_options_echo (&ireq->opt->opt, skb)) {kfree (ireq->opt); ireq->opt =NULL ; }net/ipv4/tcp_ipv4.c +19
-14@@ -152,6 +152,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)__be32 daddr, nexthop;int tmp;int err; +struct ip_options_rcu *inet_opt;if (addr_len <sizeof (struct sockaddr_in ))return -EINVAL ;@@ -160,10 +161,11 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)return -EAFNOSUPPORT ; nexthop = daddr = usin->sin_addr.s_addr; -if (inet->opt && inet->opt->srr) { + inet_opt = inet->inet_opt; +if (inet_opt && inet_opt->opt.srr) {if (!daddr)return -EINVAL ; - nexthop = inet->opt->faddr; + nexthop = inet_opt->opt.faddr; } tmp = ip_route_connect(&rt, nexthop, inet->saddr,@@ -181,7 +183,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)return -ENETUNREACH ; } -if (!inet->opt || !inet->opt->srr) +if (!inet_opt || !inet_opt->opt.srr) daddr = rt->rt_dst;if (!inet->saddr)@@ -215,8 +217,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->daddr = daddr;inet_csk (sk)->icsk_ext_hdr_len =0 ; -if (inet->opt) -inet_csk (sk)->icsk_ext_hdr_len = inet->opt->optlen; +if (inet_opt) +inet_csk (sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; tp->rx_opt.mss_clamp =536 ;@@ -802,17 +804,18 @@ static void syn_flood_warning(struct sk_buff *skb)/* * Save and compile IPv4 options into the request_sock if needed. */ -static struct ip_options *tcp_v4_save_options (struct sock *sk, -struct sk_buff *skb) +static struct ip_options_rcu *tcp_v4_save_options (struct sock *sk, +struct sk_buff *skb) { -struct ip_options *opt = &(IPCB (skb)->opt); -struct ip_options *dopt =NULL ; +const struct ip_options *opt = &(IPCB (skb)->opt); +struct ip_options_rcu *dopt =NULL ;if (opt && opt->optlen) { -int opt_size =optlength (opt); +int opt_size =sizeof (*dopt) + opt->optlen; + dopt =kmalloc (opt_size,GFP_ATOMIC );if (dopt) { -if (ip_options_echo (dopt, skb)) { +if (ip_options_echo (&dopt->opt, skb)) {kfree (dopt); dopt =NULL ; }@@ -1362,6 +1365,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,#ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key;#endif +struct ip_options_rcu *inet_opt;if (sk_acceptq_is_full (sk)) goto exit_overflow;@@ -1382,13 +1386,14 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newinet->daddr = ireq->rmt_addr; newinet->rcv_saddr = ireq->loc_addr; newinet->saddr = ireq->loc_addr; - newinet->opt = ireq->opt; + inet_opt = ireq->opt; +rcu_assign_pointer (newinet->inet_opt, inet_opt); ireq->opt =NULL ; newinet->mc_index =inet_iif (skb); newinet->mc_ttl =ip_hdr (skb)->ttl;inet_csk (newsk)->icsk_ext_hdr_len =0 ; -if (newinet->opt) -inet_csk (newsk)->icsk_ext_hdr_len = newinet->opt->optlen; +if (inet_opt) +inet_csk (newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen; newinet->id = newtp->write_seq ^ jiffies;tcp_mtup_init (newsk);net/ipv4/udp.c +16
-5@@ -592,6 +592,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,int err, is_udplite =IS_UDPLITE (sk);int corkreq = up->corkflag || msg->msg_flags&MSG_MORE ;int (*getfrag)(void *,char *,int ,int ,int ,struct sk_buff *); +struct ip_options_data opt_copy;if (len >0xFFFF )return -EMSGSIZE ;@@ -663,22 +664,32 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, free =1 ; connected =0 ; } -if (!ipc.opt) - ipc.opt = inet->opt; +if (!ipc.opt) { +struct ip_options_rcu *inet_opt; + +rcu_read_lock (); + inet_opt =rcu_dereference (inet->inet_opt); +if (inet_opt) { +memcpy (&opt_copy, inet_opt, +sizeof (*inet_opt) + inet_opt->opt.optlen); + ipc.opt = &opt_copy.opt; + } +rcu_read_unlock (); + } saddr = ipc.addr; ipc.addr = faddr = daddr; -if (ipc.opt && ipc.opt->srr) { +if (ipc.opt && ipc.opt->opt.srr) {if (!daddr)return -EINVAL ; - faddr = ipc.opt->faddr; + faddr = ipc.opt->opt.faddr; connected =0 ; } tos =RT_TOS (inet->tos);if (sock_flag (sk,SOCK_LOCALROUTE ) || (msg->msg_flags &MSG_DONTROUTE ) || - (ipc.opt && ipc.opt->is_strictroute)) { + (ipc.opt && ipc.opt->opt.is_strictroute)) { tos |=RTO_ONLINK ; connected =0 ; }net/ipv6/tcp_ipv6.c +1
-1@@ -1391,7 +1391,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,First: no IPv4 options. */ - newinet->opt =NULL ; + newinet->inet_opt =NULL ; newnp->ipv6_fl_list =NULL ;/* Clone RX bits */
Signature · 3 lines
Ben Hutchings
Theory and practice are closer in theory than in practice.
- John Levine, moderator of comp.compilersRe: [ 157/184] inet: add RCU protection to inet->opt
Quoted 26 lines · Ben Hutchings
Quoted 25 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit
f6d8bd051c391c1c0458a30b2a7abcd939329259upstream.We lack proper synchronization to manipulate
inet->optip_optionsProblem is
ip_make_skb()callsip_setup_cork()andip_setup_cork()possibly makes a copy ofipc->opt(structip_options), without any protection against another thread manipulatinginet->opt.Another thread can change
inet->optpointer and free old one under us.Use RCU to protect
inet->opt(changed toinet->inet_opt).Instead of handling atomic refcounts, just copy
ip_optionswhen necessary, to avoid cache line dirtying.We cant insert an
rcu_headin structip_optionssince its included in skb->cb[], so this patch is large because I had to introduce a newip_options_rcustructure.
Quoted 6 lines
Quoted 4 lines
Signed-off-by Eric Dumazet <[email protected]>Cc Herbert Xu <[email protected]>Signed-off-by David S. Miller <[email protected]>[dannf/bwh: backported to Debian's 2.6.32]Signed-off-by Ben Hutchings <[email protected]>
added, thank you.
willy
PATCH 158/184 tcp: allow splice() to build full TSO packets
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ This combines upstream commit
2f53384424251c06038ae612e56231b96ab610ee and the follow-on bug fix commit 35f9c09fe9c72eb8ca2b8e89a593e1c151f28fc2 ]
vmsplice()/splice(pipe, socket) call do_tcp_sendpages() one page at a time, adding at most 4096 bytes to an skb. (assuming PAGE_SIZE=4096)
The call to tcp_push() at the end of do_tcp_sendpages() forces an immediate xmit when pipe is not already filled, and tso_fragment() try to split these skb to MSS multiples.
4096 bytes are usually split in a skb with 2 MSS, and a remaining sub-mss skb (assuming MTU=1500)
This makes slow start suboptimal because many small frames are sent to qdisc/driver layers instead of big ones (constrained by cwnd and packets in flight of course)
In fact, applications using sendmsg() (adding an additional memory copy) instead of vmsplice()/splice()/sendfile() are a bit faster because of this anomaly, especially if serving small files in environments with large initial [c]wnd.
Call tcp_push() only if MSG_MORE is not set in the flags parameter.
This bit is automatically provided by splice() internals but for the last page, or on all pages if user specified SPLICE_F_MORE splice() flag.
In some workloads, this can reduce number of sent logical packets by an order of magnitude, making zero-copy TCP actually faster than one-copy :)
--- fs/splice.c | 5 ++++- include/linux/socket.h | 2 +- net/ipv4/tcp.c | 2 +- net/socket.c | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-)
fs/splice.c +4 -1
@@ -30,6 +30,7 @@#include <linux/syscalls.h> #include <linux/uio.h> #include <linux/security.h> +#include <linux/socket.h> /* * Attempt to steal a page from a pipe buffer. This should perhaps go into
@@ -637,7 +638,9 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe, ret = buf->ops->confirm (pipe, buf);if (!ret) { - more = (sd->flags &SPLICE_F_MORE ) || sd->len < sd->total_len; + more = (sd->flags &SPLICE_F_MORE ) ?MSG_MORE :0 ; +if (sd->len < sd->total_len) + more |=MSG_SENDPAGE_NOTLAST ;if (file->f_op && file->f_op->sendpage) ret = file->f_op->sendpage (file, buf->page, buf->offset, sd->len, &pos, more);
include/linux/socket.h +1 -1
@@ -246,7 +246,7 @@ struct ucred {#define MSG_ERRQUEUE 0x2000/* Fetch message from error queue */ #define MSG_NOSIGNAL 0x4000/* Do not generate SIGPIPE */ #define MSG_MORE 0x8000/* Sender will send more */ - +#define MSG_SENDPAGE_NOTLAST 0x20000/* sendpage() internal : not the last page */ #define MSG_EOF MSG_FIN#define MSG_CMSG_CLOEXEC 0x40000000/* Set close_on_exit for file
net/ipv4/tcp.c +1 -1
@@ -847,7 +847,7 @@ wait_for_memory: } out: -if (copied) +if (copied && !(flags &MSG_SENDPAGE_NOTLAST ))tcp_push (sk, flags, mss_now, tp->nonagle);return copied;
net/socket.c +3 -3
@@ -732,9 +732,9 @@ static ssize_t sock_sendpage(struct file *file, struct page *page, sock = file->private_data; - flags = !(file->f_flags &O_NONBLOCK ) ?0 :MSG_DONTWAIT ; -if (more) - flags |=MSG_MORE ; + flags = (file->f_flags &O_NONBLOCK ) ?MSG_DONTWAIT :0 ; +/* more is a combination of MSG_MORE and MSG_SENDPAGE_NOTLAST */ + flags |= more;return kernel_sendpage (sock, page, offset, size, flags); }
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 159/184 tcp: fix MSG_SENDPAGE_NOTLAST logic
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit ae62ca7b03217be5e74759dc6d7698c95df498b3 ]
commit 35f9c09fe9c72e (tcp: tcp_sendpages() should call tcp_push() once) added an internal flag : MSG_SENDPAGE_NOTLAST meant to be set on all frags but the last one for a splice() call.
The condition used to set the flag in pipe_to_sendpage() relied on splice() user passing the exact number of bytes present in the pipe, or a smaller one.
But some programs pass an arbitrary high value, and the test fails.
The effect of this bug is a lack of tcp_push() at the end of a splice(pipe -> socket) call, and possibly very slow or erratic TCP sessions.
We should both test sd->total_len and fact that another fragment is in the pipe (pipe->nrbufs > 1)
Many thanks to Willy for providing very clear bug report, bisection and test programs.
--- fs/splice.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/splice.c +3 -1
@@ -639,8 +639,10 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe, ret = buf->ops->confirm (pipe, buf);if (!ret) { more = (sd->flags &SPLICE_F_MORE ) ?MSG_MORE :0 ; -if (sd->len < sd->total_len) + +if (sd->len < sd->total_len && pipe->nrbufs >1 ) more |=MSG_SENDPAGE_NOTLAST ; +if (file->f_op && file->f_op->sendpage) ret = file->f_op->sendpage (file, buf->page, buf->offset, sd->len, &pos, more);
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 160/184 tcp: preserve ACK clocking in TSO
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit f4541d60a449afd40448b06496dcd510f505928e ]
A long standing problem with TSO is the fact that tcp_tso_should_defer() rearms the deferred timer, while it should not.
Current code leads to following bad bursty behavior :
20:11:24.484333 IP A > B: . 297161:316921(19760) ack 1 win 119 20:11:24.484337 IP B > A: . ack 263721 win 1117 20:11:24.485086 IP B > A: . ack 265241 win 1117 20:11:24.485925 IP B > A: . ack 266761 win 1117 20:11:24.486759 IP B > A: . ack 268281 win 1117 20:11:24.487594 IP B > A: . ack 269801 win 1117 20:11:24.488430 IP B > A: . ack 271321 win 1117 20:11:24.489267 IP B > A: . ack 272841 win 1117 20:11:24.490104 IP B > A: . ack 274361 win 1117 20:11:24.490939 IP B > A: . ack 275881 win 1117 20:11:24.491775 IP B > A: . ack 277401 win 1117 20:11:24.491784 IP A > B: . 316921:332881(15960) ack 1 win 119 20:11:24.492620 IP B > A: . ack 278921 win 1117 20:11:24.493448 IP B > A: . ack 280441 win 1117 20:11:24.494286 IP B > A: . ack 281961 win 1117 20:11:24.495122 IP B > A: . ack 283481 win 1117 20:11:24.495958 IP B > A: . ack 285001 win 1117 20:11:24.496791 IP B > A: . ack 286521 win 1117 20:11:24.497628 IP B > A: . ack 288041 win 1117 20:11:24.498459 IP B > A: . ack 289561 win 1117 20:11:24.499296 IP B > A: . ack 291081 win 1117 20:11:24.500133 IP B > A: . ack 292601 win 1117 20:11:24.500970 IP B > A: . ack 294121 win 1117 20:11:24.501388 IP B > A: . ack 295641 win 1117 20:11:24.501398 IP A > B: . 332881:351881(19000) ack 1 win 119
While the expected behavior is more like :
20:19:49.259620 IP A > B: . 197601:202161(4560) ack 1 win 119 20:19:49.260446 IP B > A: . ack 154281 win 1212 20:19:49.261282 IP B > A: . ack 155801 win 1212 20:19:49.262125 IP B > A: . ack 157321 win 1212 20:19:49.262136 IP A > B: . 202161:206721(4560) ack 1 win 119 20:19:49.262958 IP B > A: . ack 158841 win 1212 20:19:49.263795 IP B > A: . ack 160361 win 1212 20:19:49.264628 IP B > A: . ack 161881 win 1212 20:19:49.264637 IP A > B: . 206721:211281(4560) ack 1 win 119 20:19:49.265465 IP B > A: . ack 163401 win 1212 20:19:49.265886 IP B > A: . ack 164921 win 1212 20:19:49.266722 IP B > A: . ack 166441 win 1212 20:19:49.266732 IP A > B: . 211281:215841(4560) ack 1 win 119 20:19:49.267559 IP B > A: . ack 167961 win 1212 20:19:49.268394 IP B > A: . ack 169481 win 1212 20:19:49.269232 IP B > A: . ack 171001 win 1212 20:19:49.269241 IP A > B: . 215841:221161(5320) ack 1 win 119
--- net/ipv4/tcp_output.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/ipv4/tcp_output.c +5 -2
@@ -1391,8 +1391,11 @@ static int tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) goto send_now; } -/* Ok, it looks like it is advisable to defer. */ - tp->tso_deferred =1 | (jiffies <<1 ); +/* Ok, it looks like it is advisable to defer. +* Do not rearm the timer if already set to not break TCP ACK clocking. +*/ +if (!tp->tso_deferred) + tp->tso_deferred =1 | (jiffies <<1 );return 1 ;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 161/184 unix: fix a race condition in unix_release()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Paul Moore <[email protected]>
[ Upstream commit ded34e0fe8fe8c2d595bfa30626654e4b87621e0 ]
As reported by Jan, and others over the past few years, there is a race condition caused by unix_release setting the sock->sk pointer to NULL before properly marking the socket as dead/orphaned. This can cause a problem with the LSM hook security_unix_may_send() if there is another socket attempting to write to this partially released socket in between when sock->sk is set to NULL and it is marked as dead/orphaned. This patch fixes this by only setting sock->sk to NULL after the socket has been marked as dead; I also take the opportunity to make unix_release_sock() a void function as it only ever returned 0/success.
Dave, I think this one should go on the -stable pile.
Special thanks to Jan for coming up with a reproducer for this problem.
--- net/unix/af_unix.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
net/unix/af_unix.c +3 -4
@@ -370,7 +370,7 @@ static void unix_sock_destructor(struct sock *sk)#endif } -static int unix_release_sock (struct sock *sk,int embrion) +static void unix_release_sock (struct sock *sk,int embrion) {struct unix_sock *u =unix_sk (sk);struct dentry *dentry;
@@ -445,8 +445,6 @@ static int unix_release_sock(struct sock *sk, int embrion)if (unix_tot_inflight)unix_gc ();/* Garbage collect fds */ - -return 0 ; }static int unix_listen (struct socket *sock,int backlog)
@@ -660,9 +658,10 @@ static int unix_release(struct socket *sock)if (!sk)return 0 ; +unix_release_sock (sk,0 ); sock->sk =NULL ; -return unix_release_sock (sk,0 ); +return 0 ; }static int unix_autobind (struct socket *sock)
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 162/184 dcbnl: fix various netlink info leaks
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
commit 29cd8ae0e1a39e239a3a7b67da1986add1199fc0 upstream.
The dcb netlink interface leaks stack memory in various places:
* perm_addr[] buffer is only filled at max with 12 of the 32 bytes but copied completely,
* no in-kernel driver fills all fields of an IEEE 802.1Qaz subcommand, so we're leaking up to 58 bytes for ieee_ets structs, up to 136 bytes for ieee_pfc structs, etc.,
* the same is true for CEE -- no in-kernel driver fills the whole struct,
Prevent all of the above stack info leaks by properly initializing the buffers/structures involved.
[bwh: Backported to 2.6.32: no support for IEEE or CEE commands, so only
deal with perm_addr]
--- net/dcb/dcbnl.c | 1 + 1 file changed, 1 insertion(+)
net/dcb/dcbnl.c +1 -0
@@ -307,6 +307,7 @@ static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb, dcb->dcb_family =AF_UNSPEC ; dcb->cmd =DCB_CMD_GPERM_HWADDR ; +memset (perm_addr,0 ,sizeof (perm_addr)); netdev->dcbnl_ops->getpermhwaddr (netdev, perm_addr); ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr),
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 163/184 sctp: fix memory leak in sctp_datamsg_from_user()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
when copy from user space fails
From: Tommi Rantala <[email protected]>
[ Upstream commit be364c8c0f17a3dd42707b5a090b318028538eb9 ]
Trinity (the syscall fuzzer) discovered a memory leak in SCTP, reproducible e.g. with the sendto() syscall by passing invalid user space pointer in the second argument:
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
int main(void)
{
int fd;
struct sockaddr_in sa;
fd = socket(AF_INET, SOCK_STREAM, 132 /*IPPROTO_SCTP*/);
if (fd < 0)
return 1;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
sa.sin_port = htons(11111);
sendto(fd, NULL, 1, 0, (struct sockaddr *)&sa, sizeof(sa));
return 0;}
As far as I can tell, the leak has been around since ~2003.
--- net/sctp/chunk.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/sctp/chunk.c +5 -2
@@ -272,7 +272,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, goto errout; err =sctp_user_addto_chunk (chunk, offset, len, msgh->msg_iov);if (err <0 ) - goto errout; + goto errout_chunk_free; offset += len;
@@ -308,7 +308,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,__skb_pull (chunk->skb, (__u8 *)chunk->chunk_hdr - (__u8 *)chunk->skb->data);if (err <0 ) - goto errout; + goto errout_chunk_free;sctp_datamsg_assign (msg, chunk);list_add_tail (&chunk->frag_list, &msg->chunks);
@@ -316,6 +316,9 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,return msg; +errout_chunk_free: +sctp_chunk_free (chunk); + errout:list_for_each_safe (pos, temp, &msg->chunks) {list_del_init (pos);
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 164/184 net: sctp: sctp_setsockopt_auth_key: use kzfree
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
instead of kfree
From: Daniel Borkmann <[email protected]>
[ Upstream commit 6ba542a291a5e558603ac51cda9bded347ce7627 ]
In sctp_setsockopt_auth_key, we create a temporary copy of the user passed shared auth key for the endpoint or association and after internal setup, we free it right away. Since it's sensitive data, we should zero out the key before returning the memory back to the allocator. Thus, use kzfree instead of kfree, just as we do in sctp_auth_key_put().
--- net/sctp/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sctp/socket.c +1 -1
@@ -3271,7 +3271,7 @@ static int sctp_setsockopt_auth_key(struct sock *sk, ret =sctp_auth_set_key (sctp_sk (sk)->ep, asoc, authkey); out: -kfree (authkey); +kzfree (authkey);return ret; }
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 165/184 net: sctp: sctp_endpoint_free: zero out secret key
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
data
From: Daniel Borkmann <[email protected]>
[ Upstream commit b5c37fe6e24eec194bb29d22fdd55d73bcc709bf ]
On sctp_endpoint_destroy, previously used sensitive keying material should be zeroed out before the memory is returned, as we already do with e.g. auth keys when released.
--- net/sctp/endpointola.c | 5 +++++ 1 file changed, 5 insertions(+)
net/sctp/endpointola.c +5 -0
@@ -249,6 +249,8 @@ void sctp_endpoint_free(struct sctp_endpoint *ep)/* Final destructor for endpoint. */ static void sctp_endpoint_destroy (struct sctp_endpoint *ep) { +int i; +SCTP_ASSERT (ep->base.dead,"Endpoint is not dead" , return);/* Free up the HMAC transform. */
@@ -271,6 +273,9 @@ static void sctp_endpoint_destroy(struct sctp_endpoint *ep)sctp_inq_free (&ep->base.inqueue);sctp_bind_addr_free (&ep->base.bind_addr); +for (i =0 ; i <SCTP_HOW_MANY_SECRETS ; ++i) +memset (&ep->secret_key[i],0 ,SCTP_SECRET_SIZE ); +/* Remove and free the port */ if (sctp_sk (ep->base.sk)->bind_hash)sctp_put_port (ep->base.sk);
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 166/184 net: sctp: sctp_auth_key_put: use kzfree instead of
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
kfree
From: Daniel Borkmann <[email protected]>
[ Upstream commit 586c31f3bf04c290dc0a0de7fc91d20aa9a5ee53 ]
For sensitive data like keying material, it is common practice to zero out keys before returning the memory back to the allocator. Thus, use kzfree instead of kfree.
--- net/sctp/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sctp/auth.c +1 -1
@@ -70,7 +70,7 @@ void sctp_auth_key_put(struct sctp_auth_bytes *key)return ;if (atomic_dec_and_test (&key->refcnt)) { -kfree (key); +kzfree (key);SCTP_DBG_OBJCNT_DEC (keys); } }
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 167/184 ipv6: discard overlapping fragment
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Dichtel <[email protected]>
commit 70789d7052239992824628db8133de08dc78e593 upstream
RFC5722 prohibits reassembling fragments when some data overlaps.
Bug spotted by Zhang Zuotao <[email protected]>.
--- net/ipv6/reassembly.c | 74 +++++++++++---------------------------------------- 1 file changed, 15 insertions(+), 59 deletions(-)
net/ipv6/reassembly.c +15 -59
@@ -148,16 +148,6 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a) }EXPORT_SYMBOL (ip6_frag_match); -/* Memory Tracking Functions. */ -static inline void frag_kfree_skb (struct netns_frags *nf, -struct sk_buff *skb,int *work) -{ -if (work) - *work -= skb->truesize; -atomic_sub (skb->truesize, &nf->mem); -kfree_skb (skb); -} -void ip6_frag_init (struct inet_frag_queue *q,void *a) { struct frag_queue *fq = container_of(q, struct frag_queue, q);
@@ -348,58 +338,22 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, prev = next; } -/* We found where to put this one. Check for overlap with -* preceding fragment, and, if needed, align things so that -* any overlaps are eliminated. +/* RFC5722, Section 4: +* When reassembling an IPv6 datagram, if +* one or more its constituent fragments is determined to be an +* overlapping fragment, the entire datagram (and any constituent +* fragments, including those not yet received) MUST be silently +* discarded. */ -if (prev) { -int i = (FRAG6_CB (prev)->offset + prev->len) - offset; -if (i >0 ) { - offset += i; -if (end <= offset) - goto err; -if (!pskb_pull (skb, i)) - goto err; -if (skb->ip_summed !=CHECKSUM_UNNECESSARY ) - skb->ip_summed =CHECKSUM_NONE ; - } - } +/* Check for overlap with preceding fragment. */ +if (prev && + (FRAG6_CB (prev)->offset + prev->len) - offset >0 ) + goto discard_fq; -/* Look for overlap with succeeding segments. -* If we can merge fragments, do it. -*/ -while (next &&FRAG6_CB (next)->offset < end) { -int i = end -FRAG6_CB (next)->offset;/* overlap is 'i' bytes */ - -if (i < next->len) { -/* Eat head of the next overlapped fragment -* and leave the loop. The next ones cannot overlap. -*/ -if (!pskb_pull (next, i)) - goto err; -FRAG6_CB (next)->offset += i;/* next fragment */ - fq->q.meat -= i; -if (next->ip_summed !=CHECKSUM_UNNECESSARY ) - next->ip_summed =CHECKSUM_NONE ; -break ; - }else { -struct sk_buff *free_it = next; - -/* Old fragment is completely overridden with -* new one drop it. -*/ - next = next->next; - -if (prev) - prev->next = next; -else - fq->q.fragments = next; - - fq->q.meat -= free_it->len; -frag_kfree_skb (fq->q.net, free_it,NULL ); - } - } +/* Look for overlap with succeeding segment. */ +if (next &&FRAG6_CB (next)->offset < end) + goto discard_fq;FRAG6_CB (skb)->offset = offset;
@@ -436,6 +390,8 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,write_unlock (&ip6_frags.lock);return -1 ; +discard_fq: +fq_kill (fq); err:IP6_INC_STATS (net,ip6_dst_idev (skb_dst (skb)),IPSTATS_MIB_REASMFAILS );
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 168/184 ipv6: make fragment identifications less predictable
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Backport of upstream commit 87c48fa3b4630905f98268dde838ee43626a060c ]
Fernando Gont reported current IPv6 fragment identification generation was not secure, because using a very predictable system-wide generator, allowing various attacks.
IPv4 uses inetpeer cache to address this problem and to get good performance. We'll use this mechanism when IPv6 inetpeer is stable enough in linux-3.1
For the time being, we use jhash on destination address to provide less predictable identifications. Also remove a spinlock and use cmpxchg() to get better SMP performance.
--- include/net/ipv6.h | 12 +----------- include/net/transp_v6.h | 2 ++ net/ipv6/af_inet6.c | 2 ++ net/ipv6/ip6_output.c | 40 +++++++++++++++++++++++++++++++++++----- net/ipv6/udp.c | 2 +- 5 files changed, 41 insertions(+), 17 deletions(-)
include/net/ipv6.h +1 -11
@@ -449,17 +449,7 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_addreturn __ipv6_addr_diff (a1, a2,sizeof (struct in6_addr )); } -static __inline__void ipv6_select_ident (struct frag_hdr *fhdr) -{ -static u32 ipv6_fragmentation_id =1 ; -static DEFINE_SPINLOCK (ip6_id_lock); - -spin_lock_bh (&ip6_id_lock); - fhdr->identification =htonl (ipv6_fragmentation_id); -if (++ipv6_fragmentation_id ==0 ) - ipv6_fragmentation_id =1 ; -spin_unlock_bh (&ip6_id_lock); -} +extern void ipv6_select_ident (struct frag_hdr *fhdr,struct rt6_info *rt);/* * Prototypes exported by ipv6
include/net/transp_v6.h +2 -0
@@ -16,6 +16,8 @@ extern struct proto tcpv6_prot;struct flowi ; +extern void initialize_hashidentrnd (void ); +/* extention headers */ extern int ipv6_exthdrs_init (void );extern void ipv6_exthdrs_exit (void );
net/ipv6/af_inet6.c +2 -0
@@ -1073,6 +1073,8 @@ static int __init inet6_init(void) goto out; } +initialize_hashidentrnd (); + err =proto_register (&tcpv6_prot,1 );if (err) goto out;
net/ipv6/ip6_output.c +35 -5
@@ -604,6 +604,35 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)return offset; } +static u32 hashidentrnd__read_mostly ; +#define FID_HASH_SZ 16 +static u32 ipv6_fragmentation_id[FID_HASH_SZ ]; + +void __init initialize_hashidentrnd (void ) +{ +get_random_bytes (&hashidentrnd,sizeof (hashidentrnd)); +} + +static u32 __ipv6_select_ident (const struct in6_addr *addr) +{ +u32 newid, oldid, hash =jhash2 ((u32 *)addr,4 , hashidentrnd); +u32 *pid = &ipv6_fragmentation_id[hash %FID_HASH_SZ ]; + +do { + oldid = *pid; + newid = oldid +1 ; +if (!(hash + newid)) + newid++; + }while (cmpxchg (pid, oldid, newid) != oldid); + +return hash + newid; +} + +void ipv6_select_ident (struct frag_hdr *fhdr,struct rt6_info *rt) +{ + fhdr->identification =htonl (__ipv6_select_ident (&rt->rt6i_dst.addr)); +} +static int ip6_fragment (struct sk_buff *skb,int (*output)(struct sk_buff *)) {struct sk_buff *frag;
@@ -689,7 +718,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))skb_reset_network_header (skb);memcpy (skb_network_header (skb), tmp_hdr, hlen); -ipv6_select_ident (fh); +ipv6_select_ident (fh, rt); fh->nexthdr = nexthdr; fh->reserved =0 ; fh->frag_off =htons (IP6_MF );
@@ -835,7 +864,7 @@ slow_path: fh->nexthdr = nexthdr; fh->reserved =0 ;if (!frag_id) { -ipv6_select_ident (fh); +ipv6_select_ident (fh, rt); frag_id = fh->identification; }else fh->identification = frag_id;
@@ -1039,7 +1068,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,int getfrag (void *from,char *to,int offset,int len,int odd,struct sk_buff *skb), void *from, int length, int hh_len, int fragheaderlen, - int transhdrlen, int mtu,unsigned int flags) + int transhdrlen, int mtu,unsigned int flags, + struct rt6_info *rt) { struct sk_buff *skb;
@@ -1084,7 +1114,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,skb_shinfo (skb)->gso_size = (mtu - fragheaderlen -sizeof (struct frag_hdr )) & ~7 ;skb_shinfo (skb)->gso_type =SKB_GSO_UDP ; -ipv6_select_ident (&fhdr); +ipv6_select_ident (&fhdr, rt);skb_shinfo (skb)->ip6_frag_id = fhdr.identification;__skb_queue_tail (&sk->sk_write_queue, skb);
@@ -1233,7 +1263,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, err =ip6_ufo_append_data (sk, getfrag, from, length, hh_len, fragheaderlen, transhdrlen, mtu, - flags); + flags, rt);if (err) goto error;return 0 ;
net/ipv6/udp.c +1 -1
@@ -1162,7 +1162,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, int features) fptr = (struct frag_hdr *)(skb_network_header (skb) + unfrag_ip6hlen); fptr->nexthdr = nexthdr; fptr->reserved =0 ; -ipv6_select_ident (fptr); +ipv6_select_ident (fptr, (struct rt6_info *)skb_dst (skb));/* Fragment the skb. ipv6 header and the remaining fields of the * fragment header are updated in ipv6_gso_segment()
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 169/184 netfilter: nf_ct_ipv4: packets with wrong ihl are
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
invalid
From: Jozsef Kadlecsik <[email protected]>
commit 07153c6ec074257ade76a461429b567cff2b3a1e upstream.
It was reported that the Linux kernel sometimes logs:
klogd: [2629147.402413] kernel BUG at net / netfilter / nf_conntrack_proto_tcp.c: 447!
klogd: [1072212.887368] kernel BUG at net / netfilter / nf_conntrack_proto_tcp.c: 392
ipv4_get_l4proto() in nf_conntrack_l3proto_ipv4.c and tcp_error() in nf_conntrack_proto_tcp.c should catch malformed packets, so the errors at the indicated lines - TCP options parsing - should not happen. However, tcp_error() relies on the "dataoff" offset to the TCP header, calculated by ipv4_get_l4proto(). But ipv4_get_l4proto() does not check bogus ihl values in IPv4 packets, which then can slip through tcp_error() and get caught at the TCP options parsing routines.
The patch fixes ipv4_get_l4proto() by invalidating packets with bogus ihl value.
The patch closes netfilter bugzilla id 771.
--- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 8 ++++++++ 1 file changed, 8 insertions(+)
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +8 -0
@@ -83,6 +83,14 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, *dataoff = nhoff + (iph->ihl <<2 ); *protonum = iph->protocol; +/* Check bogus IP headers */ +if (*dataoff > skb->len) { +pr_debug ("nf_conntrack_ipv4: bogus IPv4 packet: " +"nhoff %u, ihl %u, skblen %u\n" , + nhoff, iph->ihl <<2 , skb->len); +return -NF_ACCEPT ; + } +return NF_ACCEPT ; }
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 170/184 ipvs: allow transmit of GRO aggregated skbs
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Simon Horman <[email protected]>
Attempt at allowing LVS to transmit skbs of greater than MTU length that have been aggregated by GRO and can thus be deaggregated by GSO.
--- net/netfilter/ipvs/ip_vs_xmit.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
net/netfilter/ipvs/ip_vs_xmit.c +11 -10
@@ -245,7 +245,8 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if ((skb->len > mtu) && (iph->frag_off &htons (IP_DF ))) { +if ((skb->len > mtu) && (iph->frag_off &htons (IP_DF )) && + !skb_is_gso (skb)) {ip_rt_put (rt);icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED ,htonl (mtu));IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -309,7 +310,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if (skb->len > mtu) { +if (skb->len > mtu && !skb_is_gso (skb)) {dst_release (&rt->u.dst);icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -376,7 +377,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if ((skb->len > mtu) && (iph->frag_off &htons (IP_DF ))) { +if ((skb->len > mtu) && (iph->frag_off &htons (IP_DF )) && !skb_is_gso (skb)) {ip_rt_put (rt);icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED ,htonl (mtu));IP_VS_DBG_RL_PKT (0 , pp, skb,0 ,"ip_vs_nat_xmit(): frag needed for" );
@@ -452,7 +453,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if (skb->len > mtu) { +if (skb->len > mtu && !skb_is_gso (skb)) {dst_release (&rt->u.dst);icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev); IP_VS_DBG_RL_PKT(0, pp, skb, 0,
@@ -561,8 +562,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, df |= (old_iph->frag_off &htons (IP_DF )); -if ((old_iph->frag_off &htons (IP_DF )) - && mtu <ntohs (old_iph->tot_len)) { +if ((old_iph->frag_off &htons (IP_DF ) && + mtu <ntohs (old_iph->tot_len) && !skb_is_gso (skb))) {icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED ,htonl (mtu));ip_rt_put (rt);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -671,7 +672,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,if (skb_dst (skb))skb_dst (skb)->ops->update_pmtu (skb_dst (skb), mtu); -if (mtu <ntohs (old_iph->payload_len) +sizeof (struct ipv6hdr )) { +if (mtu <ntohs (old_iph->payload_len) +sizeof (struct ipv6hdr ) && !skb_is_gso (skb)) {icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev);dst_release (&rt->u.dst);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -760,7 +761,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if ((iph->frag_off &htons (IP_DF )) && skb->len > mtu) { +if ((iph->frag_off &htons (IP_DF )) && skb->len > mtu && !skb_is_gso (skb)) {icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED ,htonl (mtu));ip_rt_put (rt);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -888,7 +889,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if ((skb->len > mtu) && (ip_hdr (skb)->frag_off &htons (IP_DF ))) { +if ((skb->len > mtu) && (ip_hdr (skb)->frag_off &htons (IP_DF )) && !skb_is_gso (skb)) {ip_rt_put (rt);icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED ,htonl (mtu));IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -963,7 +964,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if (skb->len > mtu) { +if (skb->len > mtu && !skb_is_gso (skb)) {dst_release (&rt->u.dst);icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 171/184 ipvs: IPv6 MTU checking cleanup and bugfix
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Jesper Dangaard Brouer <[email protected]>
Cleaning up the IPv6 MTU checking in the IPVS xmit code, by using a common helper function __mtu_check_toobig_v6().
The MTU check for tunnel mode can also use this helper as ntohs(old_iph->payload_len) + sizeof(struct ipv6hdr) is qual to skb->len. And the 'mtu' variable have been adjusted before calling helper.
Notice, this also fixes a bug, as the the MTU check in ip_vs_dr_xmit_v6() were missing a check for skb_is_gso().
This bug e.g. caused issues for KVM IPVS setups, where different Segmentation Offloading techniques are utilized, between guests, via the virtio driver. This resulted in very bad performance, due to the ICMPv6 "too big" messages didn't affect the sender.
--- net/netfilter/ipvs/ip_vs_xmit.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)
net/netfilter/ipvs/ip_vs_xmit.c +15 -5
@@ -64,6 +64,15 @@ __ip_vs_dst_check(struct ip_vs_dest *dest, u32 rtos, u32 cookie)return dst; } +static inline bool +__mtu_check_toobig_v6 (const struct sk_buff *skb,u32 mtu) +{ +if (skb->len > mtu && !skb_is_gso (skb)) { +return true;/* Packet size violate MTU size */ + } +return false; +} +static struct rtable *__ip_vs_get_out_rt (struct ip_vs_conn *cp,u32 rtos) {
@@ -310,7 +319,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if (skb->len > mtu && !skb_is_gso (skb)) { +if (__mtu_check_toobig_v6 (skb, mtu)) {dst_release (&rt->u.dst);icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -453,7 +462,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if (skb->len > mtu && !skb_is_gso (skb)) { +if (__mtu_check_toobig_v6 (skb, mtu)) {dst_release (&rt->u.dst);icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev); IP_VS_DBG_RL_PKT(0, pp, skb, 0,
@@ -672,7 +681,8 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,if (skb_dst (skb))skb_dst (skb)->ops->update_pmtu (skb_dst (skb), mtu); -if (mtu <ntohs (old_iph->payload_len) +sizeof (struct ipv6hdr ) && !skb_is_gso (skb)) { +/* MTU checking: Notice that 'mtu' have been adjusted before hand */ +if (__mtu_check_toobig_v6 (skb, mtu)) {icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev);dst_release (&rt->u.dst);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -814,7 +824,7 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if (skb->len > mtu) { +if (__mtu_check_toobig_v6 (skb, mtu)) {icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev);dst_release (&rt->u.dst);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
@@ -964,7 +974,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,/* MTU checking */ mtu =dst_mtu (&rt->u.dst); -if (skb->len > mtu && !skb_is_gso (skb)) { +if (__mtu_check_toobig_v6 (skb, mtu)) {dst_release (&rt->u.dst);icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev);IP_VS_DBG_RL ("%s(): frag needed\n" , __func__);
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 172/184 ipvs: fix info leak in
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
getsockopt(IP_VS_SO_GET_TIMEOUT)
From: Mathias Krause <[email protected]>
commit 2d8a041b7bfe1097af21441cb77d6af95f4f4680 upstream.
If at least one of CONFIG_IP_VS_PROTO_TCP or CONFIG_IP_VS_PROTO_UDP is not set, __ip_vs_get_timeouts() does not fully initialize the structure that gets copied to userland and that for leaks up to 12 bytes of kernel stack. Add an explicit memset(0) before passing the structure to __ip_vs_get_timeouts() to avoid the info leak.
--- net/netfilter/ipvs/ip_vs_ctl.c | 1 + 1 file changed, 1 insertion(+)
net/netfilter/ipvs/ip_vs_ctl.c +1 -0
@@ -2455,6 +2455,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) {struct ip_vs_timeout_user t; +memset (&t,0 ,sizeof (t));__ip_vs_get_timeouts (&t);if (copy_to_user (user, &t,sizeof (t)) !=0 ) ret = -EFAULT ;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 173/184 atm: update msg_namelen in vcc_recvmsg()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
[ Upstream commit 9b3e617f3df53822345a8573b6d358f6b9e5ed87 ]
The current code does not fill the msg_name member in case it is set. It also does not set the msg_namelen member to 0 and therefore makes net/socket.c leak the local, uninitialized sockaddr_storage variable to userland -- 128 bytes of kernel stack memory.
Fix that by simply setting msg_namelen to 0 as obviously nobody cared about vcc_recvmsg() not filling the msg_name in case it was set.
--- net/atm/common.c | 2 ++ 1 file changed, 2 insertions(+)
net/atm/common.c +2 -0
@@ -473,6 +473,8 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,struct sk_buff *skb;int copied, error = -EINVAL ; + msg->msg_namelen =0 ; +if (sock->state !=SS_CONNECTED )return -ENOTCONN ;if (flags & ~MSG_DONTWAIT )/* only handle MSG_DONTWAIT */
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 174/184 atm: fix info leak via getsockname()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
commit 3c0c5cfdcd4d69ffc4b9c0907cec99039f30a50a upstream.
The ATM code fails to initialize the two padding bytes of struct sockaddr_atmpvc inserted for alignment. Add an explicit memset(0) before filling the structure to avoid the info leak.
--- net/atm/pvc.c | 1 + 1 file changed, 1 insertion(+)
net/atm/pvc.c +1 -0
@@ -93,6 +93,7 @@ static int pvc_getname(struct socket *sock,struct sockaddr *sockaddr,if (!vcc->dev || !test_bit (ATM_VF_ADDR ,&vcc->flags))return -ENOTCONN ; *sockaddr_len =sizeof (struct sockaddr_atmpvc ); addr = (struct sockaddr_atmpvc *) sockaddr; +memset (addr,0 ,sizeof (*addr)); addr->sap_family =AF_ATMPVC ; addr->sap_addr.itf = vcc->dev->number; addr->sap_addr.vpi = vcc->vpi;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 175/184 atm: fix info leak in getsockopt(SO_ATMPVC)
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
commit e862f1a9b7df4e8196ebec45ac62295138aa3fc2 upstream.
The ATM code fails to initialize the two padding bytes of struct sockaddr_atmpvc inserted for alignment. Add an explicit memset(0) before filling the structure to avoid the info leak.
--- net/atm/common.c | 1 + 1 file changed, 1 insertion(+)
net/atm/common.c +1 -0
@@ -751,6 +751,7 @@ int vcc_getsockopt(struct socket *sock, int level, int optname,if (!vcc->dev || !test_bit (ATM_VF_ADDR ,&vcc->flags))return -ENOTCONN ; +memset (&pvc,0 ,sizeof (pvc)); pvc.sap_family =AF_ATMPVC ; pvc.sap_addr.itf = vcc->dev->number; pvc.sap_addr.vpi = vcc->vpi;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 176/184 ax25: fix info leak via msg_name in ax25_recvmsg()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
[ Upstream commit ef3313e84acbf349caecae942ab3ab731471f1a1 ]
When msg_namelen is non-zero the sockaddr info gets filled out, as requested, but the code fails to initialize the padding bytes of struct sockaddr_ax25 inserted by the compiler for alignment. Additionally the msg_namelen value is updated to sizeof(struct full_sockaddr_ax25) but is not always filled up to this size.
Both issues lead to the fact that the code will leak uninitialized kernel stack bytes in net/socket.c.
Fix both issues by initializing the memory with memset(0).
--- net/ax25/af_ax25.c | 1 + 1 file changed, 1 insertion(+)
net/ax25/af_ax25.c +1 -0
@@ -1654,6 +1654,7 @@ static int ax25_recvmsg(struct kiocb *iocb, struct socket *sock,ax25_address src;const unsigned char *mac =skb_mac_header (skb); +memset (sax,0 ,sizeof (struct full_sockaddr_ax25 ));ax25_addr_parse (mac +1 , skb->data - mac -1 , &src,NULL , &digi,NULL ,NULL ); sax->sax25_family =AF_AX25 ;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 177/184 isdnloop: fix and simplify isdnloop_init()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Wu Fengguang <[email protected]>
[ Upstream commit 77f00f6324cb97cf1df6f9c4aaeea6ada23abdb2 ]
Fix a buffer overflow bug by removing the revision and printk.
[ 22.016214] isdnloop-ISDN-driver Rev 1.11.6.7 [ 22.097508] isdnloop: (loop0) virtual card added [ 22.174400] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: ffffffff83244972 [ 22.174400] [ 22.436157] Pid: 1, comm: swapper Not tainted 3.5.0-bisect-00018-gfa8bbb1-dirty #129 [ 22.624071] Call Trace: [ 22.720558] [<ffffffff832448c3>] ? CallcNew+0x56/0x56 [ 22.815248] [<ffffffff8222b623>] panic+0x110/0x329 [ 22.914330] [<ffffffff83244972>] ? isdnloop_init+0xaf/0xb1 [ 23.014800] [<ffffffff832448c3>] ? CallcNew+0x56/0x56 [ 23.090763] [<ffffffff8108e24b>] __stack_chk_fail+0x2b/0x30 [ 23.185748] [<ffffffff83244972>] isdnloop_init+0xaf/0xb1
--- drivers/isdn/isdnloop/isdnloop.c | 12 ------------ 1 file changed, 12 deletions(-)
drivers/isdn/isdnloop/isdnloop.c +0 -12
@@ -15,7 +15,6 @@#include <linux/sched.h> #include "isdnloop.h" -static char *revision ="$Revision: 1.11.6.7 $" ;static char *isdnloop_id ="loop0" ;MODULE_DESCRIPTION ("ISDN4Linux: Pseudo Driver that simulates an ISDN card" );
@@ -1493,17 +1492,6 @@ isdnloop_addcard(char *id1)static int __init isdnloop_init (void ) { -char *p; -char rev[10 ]; - -if ((p =strchr (revision,':' ))) { -strcpy (rev, p +1 ); - p =strchr (rev,'$' ); - *p =0 ; - }else -strcpy (rev," ??? " ); -printk (KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n" , rev); -if (isdnloop_id)return (isdnloop_addcard (isdnloop_id));
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 178/184 iucv: Fix missing msg_namelen update in
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
iucv_sock_recvmsg()
From: Mathias Krause <[email protected]>
[ Upstream commit a5598bd9c087dc0efc250a5221e5d0e6f584ee88 ]
The current code does not fill the msg_name member in case it is set. It also does not set the msg_namelen member to 0 and therefore makes net/socket.c leak the local, uninitialized sockaddr_storage variable to userland -- 128 bytes of kernel stack memory.
Fix that by simply setting msg_namelen to 0 as obviously nobody cared about iucv_sock_recvmsg() not filling the msg_name in case it was set.
--- net/iucv/af_iucv.c | 2 ++ 1 file changed, 2 insertions(+)
net/iucv/af_iucv.c +2 -0
@@ -1160,6 +1160,8 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,struct sk_buff *skb, *rskb, *cskb;int err =0 ; + msg->msg_namelen =0 ; + if ((sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_SEVERED) && skb_queue_empty(&iucv->backlog_skb_q) && skb_queue_empty(&sk->sk_receive_queue) &&
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 179/184 llc: fix info leak via getsockname()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
[ Upstream commit 3592aaeb80290bda0f2cf0b5456c97bfc638b192 ]
The LLC code wrongly returns 0, i.e. "success", when the socket is zapped. Together with the uninitialized uaddrlen pointer argument from sys_getsockname this leads to an arbitrary memory leak of up to 128 bytes kernel stack via the getsockname() syscall.
Return an error instead when the socket is zapped to prevent the info leak. Also remove the unnecessary memset(0). We don't directly write to the memory pointed by uaddr but memcpy() a local structure at the end of the function that is properly initialized.
--- net/llc/af_llc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
net/llc/af_llc.c +1 -2
@@ -912,14 +912,13 @@ static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,struct sockaddr_llc sllc;struct sock *sk = sock->sk;struct llc_sock *llc =llc_sk (sk); -int rc =0 ; +int rc = -EBADF ;memset (&sllc,0 ,sizeof (sllc));lock_sock (sk);if (sock_flag (sk,SOCK_ZAPPED )) goto out; *uaddrlen =sizeof (sllc); -memset (uaddr,0 , *uaddrlen);if (peer) { rc = -ENOTCONN ;if (sk->sk_state !=TCP_ESTABLISHED )
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 180/184 llc: Fix missing msg_namelen update in
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
llc_ui_recvmsg()
From: Mathias Krause <[email protected]>
[ Upstream commit c77a4b9cffb6215a15196ec499490d116dfad181 ]
For stream sockets the code misses to update the msg_namelen member to 0 and therefore makes net/socket.c leak the local, uninitialized sockaddr_storage variable to userland -- 128 bytes of kernel stack memory. The msg_namelen update is also missing for datagram sockets in case the socket is shutting down during receive.
Fix both issues by setting msg_namelen to 0 early. It will be updated later if we're going to fill the msg_name member.
--- net/llc/af_llc.c | 2 ++ 1 file changed, 2 insertions(+)
net/llc/af_llc.c +2 -0
@@ -674,6 +674,8 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,int target;/* Read at least this many bytes */ long timeo; + msg->msg_namelen =0 ; +lock_sock (sk); copied = -ENOTCONN ;if (unlikely (sk->sk_type ==SOCK_STREAM && sk->sk_state ==TCP_LISTEN ))
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 181/184 rds: set correct msg_namelen
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Weiping Pan <[email protected]>
commit 06b6a1cf6e776426766298d055bb3991957d90a7 upstream
Jay Fenlason ([email protected]) found a bug,
that recvfrom() on an RDS socket can return the contents of random kernel memory to userspace if it was called with a address length larger than sizeof(struct sockaddr_in).rds_recvmsg() also fails to set the addr_len paramater properly before returning, but that's just a bug.
There are also a number of cases wher recvfrom() can return an entirely bogus address. Anything in rds_recvmsg() that returns a non-negative value but does not go through the "sin = (struct sockaddr_in *)msg->msg_name;" code path at the end of the while(1) loop will return up to 128 bytes of kernel memory to userspace.
And I write two test programs to reproduce this bug, you will see that in rds_server, fromAddr will be overwritten and the following sock_fd will be destroyed.
Yes, it is the programmer's fault to set msg_namelen incorrectly, but it is better to make the kernel copy the real length of address to user space in such case.
How to run the test programs ?
I test them on 32bit x86 system, 3.5.0-rc7.
1 compile
gcc -o rds_client rds_client.c
gcc -o rds_server rds_server.c
2 run ./rds_server on one console
3 run ./rds_client on another console
4 you will see something like:
server is waiting to receive data...
old socket fd=3
server received data from client:data from client
msg.msg_namelen=32
new socket fd=-1067277685sendmsg()
: Bad file descriptor
/***************** rds_client.c ********************/
int main(void)
{
int sock_fd;
struct sockaddr_in serverAddr;
struct sockaddr_in toAddr;
char recvBuffer[128] = "data from client";
struct msghdr msg;
struct iovec iov;
sock_fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
if (sock_fd < 0) {
perror("create socket error\n");
exit(1);
}
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
serverAddr.sin_port = htons(4001);
if (bind(sock_fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
perror("bind() error\n");
close(sock_fd);
exit(1);
}
memset(&toAddr, 0, sizeof(toAddr));
toAddr.sin_family = AF_INET;
toAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
toAddr.sin_port = htons(4000);
msg.msg_name = &toAddr;
msg.msg_namelen = sizeof(toAddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_iov->iov_base = recvBuffer;
msg.msg_iov->iov_len = strlen(recvBuffer) + 1;
msg.msg_control = 0;
msg.msg_controllen = 0;
msg.msg_flags = 0;
if (sendmsg(sock_fd, &msg, 0) == -1) {
perror("sendto() error\n");
close(sock_fd);
exit(1);
}
printf("client send data:%s\n", recvBuffer);
memset(recvBuffer, '\0', 128);
msg.msg_name = &toAddr;
msg.msg_namelen = sizeof(toAddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_iov->iov_base = recvBuffer;
msg.msg_iov->iov_len = 128;
msg.msg_control = 0;
msg.msg_controllen = 0;
msg.msg_flags = 0;
if (recvmsg(sock_fd, &msg, 0) == -1) {
perror("recvmsg() error\n");
close(sock_fd);
exit(1);
}
printf("receive data from server:%s\n", recvBuffer);
close(sock_fd);
return 0;
}/***************** rds_server.c ********************/
int main(void)
{
struct sockaddr_in fromAddr;
int sock_fd;
struct sockaddr_in serverAddr;
unsigned int addrLen;
char recvBuffer[128];
struct msghdr msg;
struct iovec iov;
sock_fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
if(sock_fd < 0) {
perror("create socket error\n");
exit(0);
}
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
serverAddr.sin_port = htons(4000);
if (bind(sock_fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
perror("bind error\n");
close(sock_fd);
exit(1);
}
printf("server is waiting to receive data...\n");
msg.msg_name = &fromAddr;
/*
* I add 16 to sizeof(fromAddr), ie 32,
* and pay attention to the definition of fromAddr,
* recvmsg() will overwrite sock_fd,
* since kernel will copy 32 bytes to userspace.
*
* If you just use sizeof(fromAddr), it works fine.
* */
msg.msg_namelen = sizeof(fromAddr) + 16;
/* msg.msg_namelen = sizeof(fromAddr); */
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_iov->iov_base = recvBuffer;
msg.msg_iov->iov_len = 128;
msg.msg_control = 0;
msg.msg_controllen = 0;
msg.msg_flags = 0;
while (1) {
printf("old socket fd=%d\n", sock_fd);
if (recvmsg(sock_fd, &msg, 0) == -1) {
perror("recvmsg() error\n");
close(sock_fd);
exit(1);
}
printf("server received data from client:%s\n", recvBuffer);
printf("msg.msg_namelen=%d\n", msg.msg_namelen);
printf("new socket fd=%d\n", sock_fd);
strcat(recvBuffer, "--data from server");
if (sendmsg(sock_fd, &msg, 0) == -1) {
perror("sendmsg()\n");
close(sock_fd);
exit(1);
}
}
close(sock_fd);
return 0;
}--- net/rds/recv.c | 3 +++ 1 file changed, 3 insertions(+)
net/rds/recv.c +3 -0
@@ -410,6 +410,8 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,rdsdebug ("size %zu flags 0x%x timeo %ld\n" , size, msg_flags, timeo); + msg->msg_namelen =0 ; +if (msg_flags &MSG_OOB ) goto out;
@@ -486,6 +488,7 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, sin->sin_port = inc->i_hdr.h_sport; sin->sin_addr.s_addr = inc->i_saddr;memset (sin->sin_zero,0 ,sizeof (sin->sin_zero)); + msg->msg_namelen =sizeof (*sin); }break ; }
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 182/184 rose: fix info leak via msg_name in rose_recvmsg()
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
From: Mathias Krause <[email protected]>
[ Upstream commit 4a184233f21645cf0b719366210ed445d1024d72 ]
The code in rose_recvmsg() does not initialize all of the members of struct sockaddr_rose/full_sockaddr_rose when filling the sockaddr info. Nor does it initialize the padding bytes of the structure inserted by the compiler for alignment. This will lead to leaking uninitialized kernel stack bytes in net/socket.c.
Fix the issue by initializing the memory used for sockaddr info with memset(0).
--- net/rose/af_rose.c | 1 + 1 file changed, 1 insertion(+)
net/rose/af_rose.c +1 -0
@@ -1275,6 +1275,7 @@ static int rose_recvmsg(struct kiocb *iocb, struct socket *sock,skb_copy_datagram_iovec (skb,0 , msg->msg_iov, copied);if (srose !=NULL ) { +memset (srose,0 , msg->msg_namelen); srose->srose_family =AF_ROSE ; srose->srose_addr = rose->dest_addr; srose->srose_call = rose->dest_call;
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
PATCH 183/184 irda: Fix missing msg_namelen update in
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
irda_recvmsg_dgram()
From: Mathias Krause <[email protected]>
The current code does not fill the msg_name member in case it is set. It also does not set the msg_namelen member to 0 and therefore makes net/socket.c leak the local, uninitialized sockaddr_storage variable to userland -- 128 bytes of kernel stack memory.
Fix that by simply setting msg_namelen to 0 as obviously nobody cared about irda_recvmsg_dgram() not filling the msg_name in case it was set.
--- net/irda/af_irda.c | 2 ++ 1 file changed, 2 insertions(+)
net/irda/af_irda.c +2 -0
@@ -1338,6 +1338,8 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock,if ((err =sock_error (sk)) <0 )return err; + msg->msg_namelen =0 ; + skb =skb_recv_datagram (sk, flags & ~MSG_DONTWAIT , flags &MSG_DONTWAIT , &err);if (!skb)
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
Re: [ 183/184] irda: Fix missing msg_namelen update in
Quoted 6 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
irda_recvmsg_dgram()From: Mathias Krause <[email protected]>
commit 5ae94c0d2f0bed41d6718be743985d61b7f5c47d upstream.
Quoted 31 lines
The current code does not fill the
msg_namemember in case it is set. It also does not set themsg_namelenmember to 0 and therefore makesnet/socket.cleak the local, uninitializedsockaddr_storagevariable to userland -- 128 bytes of kernel stack memory.Fix that by simply setting
msg_namelento 0 as obviously nobody cared aboutirda_recvmsg_dgram()not filling themsg_namein case it was set.Cc Samuel Ortiz <[email protected]>Signed-off-by Mathias Krause <[email protected]>Signed-off-by David S. Miller <[email protected]>[dannf: adjusted to apply to Debian's 2.6.32]Signed-off-by Willy Tarreau <[email protected]>--- net/irda/af_irda.c | 2 ++ 1 file changed, 2 insertions(+)net/irda/af_irda.c +2
-0@@ -1338,6 +1338,8 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock,if ((err =sock_error (sk)) <0 )return err; + msg->msg_namelen =0 ; + skb =skb_recv_datagram (sk, flags & ~MSG_DONTWAIT , flags &MSG_DONTWAIT , &err);if (!skb)
Signature · 3 lines
Ben Hutchings
Theory and practice are closer in theory than in practice.
- John Levine, moderator of comp.compilersRe: [ 183/184] irda: Fix missing msg_namelen update in
Quoted 9 lines · Ben Hutchings
Quoted 6 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
irda_recvmsg_dgram()From: Mathias Krause <[email protected]>
commit
5ae94c0d2f0bed41d6718be743985d61b7f5c47dupstream.
Added, thanks!
Willy
PATCH 184/184 tipc: fix info leaks via msg_name in
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
recv_msg/recv_stream
From: Mathias Krause <[email protected]>
The code in set_orig_addr() does not initialize all of the members of struct sockaddr_tipc when filling the sockaddr info -- namely the union is only partly filled. This will make recv_msg() and recv_stream() -- the only users of this function -- leak kernel stack memory as the msg_name member is a local variable in net/socket.c.
Additionally to that both recv_msg() and recv_stream() fail to update the msg_namelen member to 0 while otherwise returning with 0, i.e. "success". This is the case for, e.g., non-blocking sockets. This will lead to a 128 byte kernel stack leak in net/socket.c.
Fix the first issue by initializing the memory of the union with memset(0). Fix the second one by setting msg_namelen to 0 early as it will be updated later if we're going to fill the msg_name member.
--- net/tipc/socket.c | 7 +++++++ 1 file changed, 7 insertions(+)
net/tipc/socket.c +7 -0
@@ -800,6 +800,7 @@ static void set_orig_addr(struct msghdr *m, struct tipc_msg *msg)if (addr) { addr->family =AF_TIPC ; addr->addrtype =TIPC_ADDR_ID ; +memset (&addr->addr,0 ,sizeof (addr->addr)); addr->addr.id.ref =msg_origport (msg); addr->addr.id.node =msg_orignode (msg); addr->addr.name.domain =0 ;/* could leave uninitialized */
@@ -916,6 +917,9 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock, goto exit; } +/* will be updated in set_orig_addr() if needed */ + m->msg_namelen =0 ; + restart:/* Look for a message in receive queue; wait if necessary */
@@ -1049,6 +1053,9 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock, goto exit; } +/* will be updated in set_orig_addr() if needed */ + m->msg_namelen =0 ; + restart:/* Look for a message in receive queue; wait if necessary */
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
Re: [ 184/184] tipc: fix info leaks via msg_name in
Quoted 6 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
recv_msg/recv_streamFrom: Mathias Krause <[email protected]>
commit 60085c3d009b0df252547adb336d1ccca5ce52ec upstream.
Quoted 57 lines
The code in
set_orig_addr()does not initialize all of the members of structsockaddr_tipcwhen filling the sockaddr info -- namely the union is only partly filled. This will makerecv_msg()andrecv_stream()-- the only users of this function -- leak kernel stack memory as themsg_namemember is a local variable innet/socket.c.Additionally to that both
recv_msg()andrecv_stream()fail to update themsg_namelenmember to 0 while otherwise returning with 0, i.e. "success". This is the case for, e.g., non-blocking sockets. This will lead to a 128 byte kernel stack leak innet/socket.c.Fix the first issue by initializing the memory of the union with memset(0). Fix the second one by setting
msg_namelento 0 early as it will be updated later if we're going to fill themsg_namemember.Cc Jon Maloy <[email protected]>Cc Allan Stephens <[email protected]>Signed-off-by Mathias Krause <[email protected]>Signed-off-by David S. Miller <[email protected]>[dannf: backported to Debian's 2.6.32]Signed-off-by Willy Tarreau <[email protected]>--- net/tipc/socket.c | 7 +++++++ 1 file changed, 7 insertions(+)net/tipc/socket.c +7
-0@@ -800,6 +800,7 @@ static void set_orig_addr(struct msghdr *m, struct tipc_msg *msg)if (addr) { addr->family =AF_TIPC ; addr->addrtype =TIPC_ADDR_ID ; +memset (&addr->addr,0 ,sizeof (addr->addr)); addr->addr.id.ref =msg_origport (msg); addr->addr.id.node =msg_orignode (msg); addr->addr.name.domain =0 ;/* could leave uninitialized */ @@ -916,6 +917,9 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock, goto exit; } +/* will be updated in set_orig_addr() if needed */ + m->msg_namelen =0 ; + restart:/* Look for a message in receive queue; wait if necessary */ @@ -1049,6 +1053,9 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock, goto exit; } +/* will be updated in set_orig_addr() if needed */ + m->msg_namelen =0 ; + restart:/* Look for a message in receive queue; wait if necessary */
Signature · 3 lines
Ben Hutchings
Theory and practice are closer in theory than in practice.
- John Levine, moderator of comp.compilersRe: [ 184/184] tipc: fix info leaks via msg_name in
Quoted 9 lines · Ben Hutchings
Quoted 6 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know.
------------------
recv_msg/recv_streamFrom: Mathias Krause <[email protected]>
commit
60085c3d009b0df252547adb336d1ccca5ce52ecupstream.
Thank you Ben
Willy
[ 185/184] [SCSI] mpt2sas: Send default descriptor for RAID pass through in mpt2ctl
In reply to a message not in the archive2.6.32-longterm review patch. If anyone has any objections, please let me know. Thanks to Moritz for spotting this missing patch from the series.
------------------
From: "Kashyap, Desai" <[email protected]>
RAID_SCSI_IO_PASSTHROUGH: Driver needs to be sending the default descriptor for RAID Passthru, currently its sending SCSI_IO descriptor.
--- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/scsi/mpt2sas/mpt2sas_ctl.c +5 -2
@@ -744,8 +744,11 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,mpt2sas_base_get_sense_buffer_dma (ioc, smid); priv_sense =mpt2sas_base_get_sense_buffer (ioc, smid);memset (priv_sense,0 ,SCSI_SENSE_BUFFERSIZE ); -mpt2sas_base_put_smid_scsi_io (ioc, smid, -le16_to_cpu (mpi_request->FunctionDependent1)); +if (mpi_request->Function ==MPI2_FUNCTION_SCSI_IO_REQUEST ) +mpt2sas_base_put_smid_scsi_io (ioc, smid, +le16_to_cpu (mpi_request->FunctionDependent1)); +else +mpt2sas_base_put_smid_default (ioc, smid);break ; }case MPI2_FUNCTION_SCSI_TASK_MGMT :
Signature · 1 line
1.7.12.2.21.g234cd45.dirty
Re: [ 185/184] [SCSI] mpt2sas: Send default descriptor for RAID pass through in mpt2ctl
Quoted 6 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know. Thanks to Moritz for spotting this missing patch from the series.
------------------
From: "Kashyap, Desai" <[email protected]>
commit ebda4d38df542e1ff4747c4daadfc7da250b4fa6 upstream.
Quoted 27 lines
RAID_SCSI_IO_PASSTHROUGH: Driver needs to be sending the default descriptor for RAID Passthru, currently its sendingSCSI_IOdescriptor.Signed-off-by Kashyap Desai <[email protected]>Signed-off-by James Bottomley <[email protected]>--- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)drivers/scsi/mpt2sas/mpt2sas_ctl.c +5
-2@@ -744,8 +744,11 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,mpt2sas_base_get_sense_buffer_dma (ioc, smid); priv_sense =mpt2sas_base_get_sense_buffer (ioc, smid);memset (priv_sense,0 ,SCSI_SENSE_BUFFERSIZE ); -mpt2sas_base_put_smid_scsi_io (ioc, smid, -le16_to_cpu (mpi_request->FunctionDependent1)); +if (mpi_request->Function ==MPI2_FUNCTION_SCSI_IO_REQUEST ) +mpt2sas_base_put_smid_scsi_io (ioc, smid, +le16_to_cpu (mpi_request->FunctionDependent1)); +else +mpt2sas_base_put_smid_default (ioc, smid);break ; }case MPI2_FUNCTION_SCSI_TASK_MGMT :
Signature · 3 lines
Ben Hutchings
Theory and practice are closer in theory than in practice.
- John Levine, moderator of comp.compilersRe: [ 185/184] [SCSI] mpt2sas: Send default descriptor for RAID pass through in mpt2ctl
Quoted 9 lines · Ben Hutchings
Quoted 6 lines · Willy Tarreau
2.6.32-longterm review patch. If anyone has any objections, please let me know. Thanks to Moritz for spotting this missing patch from the series.
------------------
From: "Kashyap, Desai" <[email protected]>
commit
ebda4d38df542e1ff4747c4daadfc7da250b4fa6upstream.
Added, thank you Ben.
Willy