Index: wp-includes/widgets.php
===================================================================
--- wp-includes/widgets.php	(revision 6715)
+++ wp-includes/widgets.php	(working copy)
@@ -159,7 +159,7 @@
 /* $options: height, width, id_base
  *   height: never used
  *   width:  width of fully expanded control form.  Try hard to use the default width.
- *   id_base: for widgets which allow multiple instances (such as the text widget), an id_base must be provided.
+ *   id_base: for multi-widgets (widgets which allow multiple instances such as the text widget), an id_base must be provided.
  *            the widget id will ennd up looking like {$id_base}-{$unique_number}
  */
 function wp_register_widget_control($id, $name, $control_callback, $options = array()) {
@@ -1389,7 +1389,7 @@
 
 add_action('init', 'wp_widgets_init', 1);
 
-/* Pattern for widget which allows multiple instances (such as the text widget)
+/* Pattern for multi-widget (allows multiple instances such as the text widget).
 
 // Displays widget on blag
 // $widget_args: number
Index: wp-admin/includes/widgets.php
===================================================================
--- wp-admin/includes/widgets.php	(revision 6715)
+++ wp-admin/includes/widgets.php	(working copy)
@@ -21,9 +21,8 @@
 		$no_widgets_shown = true;
 		$already_shown = array();
 		foreach ( $wp_registered_widgets as $name => $widget ) :
-			if ( in_array( $widget['callback'], $already_shown ) )
+			if ( in_array( $widget['callback'], $already_shown ) ) // We already showed this multi-widget
 				continue;
-			$already_shown[] = $widget['callback'];
 
 			if ( $search_terms ) {
 				$hit = false;
@@ -52,6 +51,7 @@
 			ob_end_clean();
 
 			if ( !$sidebar || false !== strpos( $widget_control_template, '%i%' ) ) {
+				$already_shown[] = $widget['callback']; // it's a multi-widget.  We only need to show it in the list once.
 				$action = 'add'; 
 				$add_url = wp_nonce_url( add_query_arg( array(
 					'sidebar' => $sidebar,
@@ -66,7 +66,7 @@
 					'edit' => $widget['id'],
 					'key' => array_search( $widget['id'], $sidebars_widgets[$sidebar] ),
 				) );
-				$widget_control_template = "<textarea>$widget_control_template</textarea>";
+				$widget_control_template = '<textarea>' . htmlspecialchars( $widget_control_template ) . '</textarea>';
 			}
 
 			$no_widgets_shown = false;
Index: wp-admin/includes/dashboard.php
===================================================================
--- wp-admin/includes/dashboard.php	(revision 6715)
+++ wp-admin/includes/dashboard.php	(working copy)
@@ -108,7 +108,7 @@
 	// Filter widget order
 	$dashboard_widgets = apply_filters( 'wp_dashboard_widgets', $dashboard_widgets );
 
-	$wp_dashboard_sidebars = array( 'wp_dashboard' => $dashboard_widgets );
+	$wp_dashboard_sidebars = array( 'wp_dashboard' => $dashboard_widgets, 'array_version' => 3.5 );
 
 	add_filter( 'dynamic_sidebar_params', 'wp_dashboard_dynamic_sidebar_params' );
 
Index: wp-admin/widgets.php
===================================================================
--- wp-admin/widgets.php	(revision 6715)
+++ wp-admin/widgets.php	(working copy)
@@ -52,11 +52,11 @@
 
 	/* Hack #1
 	 * The widget_control is overloaded.  It updates the widget's options AND echoes out the widget's HTML form.
-	 * Since we want to update before sending out any headers, we have to catchi it with an output buffer
+	 * Since we want to update before sending out any headers, we have to catch it with an output buffer,
 	 */
 	ob_start();
 		/* There can be multiple widgets of the same type, but the widget_control for that
-		 * widget type needs only be called once.
+		 * widget type needs only be called once if it's a multi-widget.
 		 */
 		$already_done = array();
 
@@ -64,8 +64,14 @@
 			if ( in_array( $control['callback'], $already_done ) )
 				continue;
 
-			if ( is_callable( $control['callback'] ) )
+			if ( is_callable( $control['callback'] ) ) {
 				call_user_func_array( $control['callback'], $control['params'] );
+				$control_output = ob_get_contents();
+				if ( false !== strpos( $control_output, '%i%' ) ) // if it's a multi-widget, only call control function once.
+					$already_done[] = $control['callback'];
+			}
+
+			ob_clean();
 		}
 	ob_end_clean();
 
@@ -75,7 +81,7 @@
 			unset($_POST['widget-id'][$key]);
 
 	// Reset the key numbering and stare
-	$new_sidebar = array_values( $_POST['widget-id'] );
+	$new_sidebar = isset( $_POST['widget-id'] ) && is_array( $_POST['widget-id'] ) ? array_values( $_POST['widget-id'] ) : array();
 	$sidebars_widgets[$_POST['sidebar']] = $new_sidebar;
 	wp_set_sidebars_widgets( $sidebars_widgets );
 
