mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	sctp: Fix oops when sending queued ASCONF chunks
When we finish processing ASCONF_ACK chunk, we try to send the next queued ASCONF. This action runs the sctp state machine recursively and it's not prepared to do so. kernel BUG at kernel/timer.c:790! invalid opcode: 0000 [#1] SMP last sysfs file: /sys/module/ipv6/initstate Modules linked in: sha256_generic sctp libcrc32c ipv6 dm_multipath uinput 8139too i2c_piix4 8139cp mii i2c_core pcspkr virtio_net joydev floppy virtio_blk virtio_pci [last unloaded: scsi_wait_scan] Pid: 0, comm: swapper Not tainted 2.6.34-rc4 #15 /Bochs EIP: 0060:[<c044a2ef>] EFLAGS: 00010286 CPU: 0 EIP is at add_timer+0xd/0x1b EAX: cecbab14 EBX: 000000f0 ECX: c0957b1c EDX: 03595cf4 ESI: cecba800 EDI: cf276f00 EBP: c0957aa0 ESP: c0957aa0 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 Process swapper (pid: 0, ti=c0956000 task=c0988ba0 task.ti=c0956000) Stack: c0957ae0 d1851214 c0ab62e4 c0ab5f26 0500ffff 00000004 00000005 00000004 <0> 00000000 d18694fd 00000004 1666b892 cecba800 cecba800 c0957b14 00000004 <0> c0957b94 d1851b11 ceda8b00 cecba800 cf276f00 00000001 c0957b14 000000d0 Call Trace: [<d1851214>] ? sctp_side_effects+0x607/0xdfc [sctp] [<d1851b11>] ? sctp_do_sm+0x108/0x159 [sctp] [<d1863386>] ? sctp_pname+0x0/0x1d [sctp] [<d1861a56>] ? sctp_primitive_ASCONF+0x36/0x3b [sctp] [<d185657c>] ? sctp_process_asconf_ack+0x2a4/0x2d3 [sctp] [<d184e35c>] ? sctp_sf_do_asconf_ack+0x1dd/0x2b4 [sctp] [<d1851ac1>] ? sctp_do_sm+0xb8/0x159 [sctp] [<d1863334>] ? sctp_cname+0x0/0x52 [sctp] [<d1854377>] ? sctp_assoc_bh_rcv+0xac/0xe1 [sctp] [<d1858f0f>] ? sctp_inq_push+0x2d/0x30 [sctp] [<d186329d>] ? sctp_rcv+0x797/0x82e [sctp] Tested-by: Wei Yongjun <yjwei@cn.fujitsu.com> Signed-off-by: Yuansong Qiao <ysqiao@research.ait.ie> Signed-off-by: Shuaijun Zhang <szhang@research.ait.ie> Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									a8170c35e7
								
							
						
					
					
						commit
						c078669340
					
				
					 4 changed files with 34 additions and 16 deletions
				
			
		| 
						 | 
					@ -107,6 +107,7 @@ typedef enum {
 | 
				
			||||||
	SCTP_CMD_T1_RETRAN,	 /* Mark for retransmission after T1 timeout  */
 | 
						SCTP_CMD_T1_RETRAN,	 /* Mark for retransmission after T1 timeout  */
 | 
				
			||||||
	SCTP_CMD_UPDATE_INITTAG, /* Update peer inittag */
 | 
						SCTP_CMD_UPDATE_INITTAG, /* Update peer inittag */
 | 
				
			||||||
	SCTP_CMD_SEND_MSG,	 /* Send the whole use message */
 | 
						SCTP_CMD_SEND_MSG,	 /* Send the whole use message */
 | 
				
			||||||
 | 
						SCTP_CMD_SEND_NEXT_ASCONF, /* Send the next ASCONF after ACK */
 | 
				
			||||||
	SCTP_CMD_LAST
 | 
						SCTP_CMD_LAST
 | 
				
			||||||
} sctp_verb_t;
 | 
					} sctp_verb_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3318,21 +3318,6 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
 | 
				
			||||||
	sctp_chunk_free(asconf);
 | 
						sctp_chunk_free(asconf);
 | 
				
			||||||
	asoc->addip_last_asconf = NULL;
 | 
						asoc->addip_last_asconf = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Send the next asconf chunk from the addip chunk queue. */
 | 
					 | 
				
			||||||
	if (!list_empty(&asoc->addip_chunk_list)) {
 | 
					 | 
				
			||||||
		struct list_head *entry = asoc->addip_chunk_list.next;
 | 
					 | 
				
			||||||
		asconf = list_entry(entry, struct sctp_chunk, list);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		list_del_init(entry);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Hold the chunk until an ASCONF_ACK is received. */
 | 
					 | 
				
			||||||
		sctp_chunk_hold(asconf);
 | 
					 | 
				
			||||||
		if (sctp_primitive_ASCONF(asoc, asconf))
 | 
					 | 
				
			||||||
			sctp_chunk_free(asconf);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			asoc->addip_last_asconf = asconf;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return retval;
 | 
						return retval;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -962,6 +962,29 @@ static int sctp_cmd_send_msg(struct sctp_association *asoc,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Sent the next ASCONF packet currently stored in the association.
 | 
				
			||||||
 | 
					 * This happens after the ASCONF_ACK was succeffully processed.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void sctp_cmd_send_asconf(struct sctp_association *asoc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Send the next asconf chunk from the addip chunk
 | 
				
			||||||
 | 
						 * queue.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (!list_empty(&asoc->addip_chunk_list)) {
 | 
				
			||||||
 | 
							struct list_head *entry = asoc->addip_chunk_list.next;
 | 
				
			||||||
 | 
							struct sctp_chunk *asconf = list_entry(entry,
 | 
				
			||||||
 | 
											struct sctp_chunk, list);
 | 
				
			||||||
 | 
							list_del_init(entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Hold the chunk until an ASCONF_ACK is received. */
 | 
				
			||||||
 | 
							sctp_chunk_hold(asconf);
 | 
				
			||||||
 | 
							if (sctp_primitive_ASCONF(asoc, asconf))
 | 
				
			||||||
 | 
								sctp_chunk_free(asconf);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								asoc->addip_last_asconf = asconf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* These three macros allow us to pull the debugging code out of the
 | 
					/* These three macros allow us to pull the debugging code out of the
 | 
				
			||||||
 * main flow of sctp_do_sm() to keep attention focused on the real
 | 
					 * main flow of sctp_do_sm() to keep attention focused on the real
 | 
				
			||||||
| 
						 | 
					@ -1617,6 +1640,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			error = sctp_cmd_send_msg(asoc, cmd->obj.msg);
 | 
								error = sctp_cmd_send_msg(asoc, cmd->obj.msg);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							case SCTP_CMD_SEND_NEXT_ASCONF:
 | 
				
			||||||
 | 
								sctp_cmd_send_asconf(asoc);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			printk(KERN_WARNING "Impossible command: %u, %p\n",
 | 
								printk(KERN_WARNING "Impossible command: %u, %p\n",
 | 
				
			||||||
			       cmd->verb, cmd->obj.ptr);
 | 
								       cmd->verb, cmd->obj.ptr);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3676,8 +3676,14 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
 | 
				
			||||||
				SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
 | 
									SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!sctp_process_asconf_ack((struct sctp_association *)asoc,
 | 
							if (!sctp_process_asconf_ack((struct sctp_association *)asoc,
 | 
				
			||||||
					     asconf_ack))
 | 
										     asconf_ack)) {
 | 
				
			||||||
 | 
								/* Successfully processed ASCONF_ACK.  We can
 | 
				
			||||||
 | 
								 * release the next asconf if we have one.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								sctp_add_cmd_sf(commands, SCTP_CMD_SEND_NEXT_ASCONF,
 | 
				
			||||||
 | 
										SCTP_NULL());
 | 
				
			||||||
			return SCTP_DISPOSITION_CONSUME;
 | 
								return SCTP_DISPOSITION_CONSUME;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		abort = sctp_make_abort(asoc, asconf_ack,
 | 
							abort = sctp_make_abort(asoc, asconf_ack,
 | 
				
			||||||
					sizeof(sctp_errhdr_t));
 | 
										sizeof(sctp_errhdr_t));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue