diff --git a/core/includes/form.inc b/core/includes/form.inc
index a555bf0..0f1f641 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -1785,6 +1785,13 @@ function form_builder($form_id, &$element, &$form_state) {
   if (!empty($element['#description'])) {
     $element['#attributes']['aria-describedby'] = $element['#id'] . '--description';
   }
+  // Allow elements to be preprocessed before input handling.
+  if (isset($element['#preprocess']) && empty($element['#preprocessed'])) {
+    foreach ($element['#preprocess'] as $function) {
+      $function($element, $form_state, $form_state['complete_form']);
+    }
+    $element['#preprocessed'] = TRUE;
+  }
   // Handle input elements.
   if (!empty($element['#input'])) {
     _form_builder_handle_input_element($form_id, $element, $form_state);
diff --git a/core/modules/config/config.module b/core/modules/config/config.module
index b3d9bbc..8883a20 100644
--- a/core/modules/config/config.module
+++ b/core/modules/config/config.module
@@ -1 +1,135 @@
 <?php
+
+/**
+ * @file
+ * Module-level integration functionality for the configuration system.
+ */
+
+/**
+ * Retrieves, wraps, and preprocesses a configuration settings form.
+ *
+ * Use this function instead of drupal_get_form() for all settings form
+ * constructors that allow to manage module settings.
+ *
+ * @see config_settings_form_submit()
+ * @see drupal_get_form()
+ * @ingroup forms
+ */
+function config_get_form($form_id) {
+  $form_state = array();
+
+  // Prepare arguments for the form constructor (without $form_id).
+  $args = func_get_args();
+  array_shift($args);
+  $form_state['build_info']['args'] = $args;
+
+  // All configuration forms use 'config_settings_form' as base $form_id,
+  // config_settings_form_submit() is automatically invoked.
+  // This also allows modules and themes to alter all settings forms via
+  // hook_form_config_settings_form_alter() and theme_config_settings_form().
+  // @see drupal_retrieve_form()
+  // @see drupal_prepare_form()
+  // @see hook_forms()
+  $form_state['build_info']['base_form_id'] = 'config_settings_form';
+
+  // Set a wrapper callback to inject the default form structure for all
+  // settings forms.
+  // @see drupal_retrieve_form()
+  // @see hook_forms()
+  $form_state['wrapper_callback'] = 'config_settings_form_wrapper';
+
+  return drupal_build_form($form_id, $form_state);
+}
+
+/**
+ * Wrapper callback for all configuration settings forms.
+ *
+ * @see config_get_form()
+ * @ingroup forms
+ */
+function config_settings_form_wrapper($form, &$form_state) {
+  // Fill in #default_value for all elements.
+  $form['#preprocess'][] = 'config_settings_form_preprocess';
+
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save configuration'),
+  );
+
+  return $form;
+}
+
+/**
+ * #preprocess callback for configuration settings forms (and all elements within).
+ *
+ * Sets #default_value for elements with a #config property.
+ *
+ * @see config_get_form()
+ */
+function config_settings_form_preprocess(&$element, &$form_state) {
+  if (isset($element['#config']) && !isset($element['#default_value'])) {
+    // Extract and load the configuration object name and key to use.
+    list($config_name, $config_key) = explode(':', $element['#config']);
+    if (!isset($form_state['config'][$config_name])) {
+      $form_state['config'][$config_name] = config($config_name);
+    }
+    // Set the default value.
+    $element['#default_value'] = $form_state['config'][$config_name]->get($config_key);
+  }
+  // Apply this #preprocess callback to all children (recursively).
+  foreach (element_children($element) as $key) {
+    $element[$key]['#preprocess'][] = 'config_settings_form_preprocess';
+  }
+}
+
+/**
+ * Form submission handler for configuration settings forms.
+ *
+ * @see config_get_form()
+ *
+ * @todo Add (back) element property to array_filter() checkboxes?
+ */
+function config_settings_form_submit($form, &$form_state) {
+  // Exclude unnecessary elements.
+  form_state_values_clean($form_state);
+
+  // Process submitted form values and set them on configuration objects.
+  _config_settings_form_submit($form, $form_state);
+
+  // Save configuration objects.
+  // $form_state['config'] must be an array containing configuration objects at
+  // this point or something (else) went completely wrong.
+  foreach ($form_state['config'] as $config) {
+    $config->save();
+  }
+
+  drupal_set_message(t('The configuration options have been saved.'));
+}
+
+/**
+ * Helper function for config_settings_form_submit() to process configuration data.
+ */
+function _config_settings_form_submit($form, &$form_state) {
+  foreach (element_children($form) as $key) {
+    if (isset($form[$key]['#config'])) {
+      // Extract configuration object name and key to use.
+      list($config_name, $config_key) = explode(':', $form[$key]['#config']);
+
+      // Load the configuration object if we have not already.
+      if (!isset($form_state['config'][$config_name])) {
+        $form_state['config'][$config_name] = config($config_name);
+      }
+
+      // Set the new configuration value.
+      $value = drupal_array_get_nested_value($form_state['values'], $form[$key]['#parents']);
+      $form_state['config'][$config_name]->set($config_key, $value);
+
+      // Unset the submitted value, so it is not saved as variable.
+      drupal_array_unset_nested_value($form_state['values'], $form[$key]['#parents']);
+    }
+    // Recurse into all child elements.
+    _config_settings_form_submit($form[$key], $form_state);
+  }
+}
+
diff --git a/core/modules/config/config.test b/core/modules/config/config.test
index 591e378..90608eb 100644
--- a/core/modules/config/config.test
+++ b/core/modules/config/config.test
@@ -229,6 +229,114 @@ class ConfigFileContentTestCase extends WebTestBase {
   }
 }
 
+/**
+ * Tests configuration settings forms.
+ */
+class ConfigSettingsFormTestCase extends WebTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Settings forms',
+      'description' => 'Tests configuration settings forms.',
+      'group' => 'Configuration',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('config_test'));
+  }
+
+  /**
+   * Tests config_get_form().
+   *
+   * - There is no direct mapping of $form structure keys to $config keys.
+   * - There is no direct mapping of input element names to $config keys.
+   * - There is no direct mapping of $form_state['values'] to $config keys.
+   * - Nevertheless, settings forms need to support all possible variations of
+   *   #tree and #parents.
+   * - Settings forms may be extended by other modules, but their values must
+   *   only be written to their module-specific configuration objects.
+   * - Saving a settings form without additional input updates the
+   *   configuration, but does not change any values.
+   */
+  function testSettingsForm() {
+    // Load default configuration.
+    $config_settings = config('config_test.settings');
+    $config_other = config('config_test.other');
+    $this->assertTrue($config_settings->get());
+    $this->assertTrue($config_other->get());
+    $this->assertNoVariables($config_settings->get());
+    $this->assertNoVariables($config_other->get());
+
+    // Verify that default values remain the same when not changing them.
+    $this->drupalPost('config_test/system_settings_form', array(), t('Save configuration'));
+    $new_config_settings = config('config_test.settings');
+    $new_config_other = config('config_test.other');
+    $this->assertIdentical($config_settings->get(), $new_config_settings->get());
+    $this->assertIdentical($config_other->get(), $new_config_other->get());
+    $this->assertNoVariables($new_config_settings->get());
+    $this->assertNoVariables($new_config_other->get());
+
+    // Change the configuration.
+    $edit = array(
+      'simple' => FALSE,
+      'set[first]' => 'changed-first',
+      'set[second]' => 'changed-second',
+      'set[third]' => 'changed-third',
+      'lost_child' => 'Geek',
+    );
+    $this->drupalPost(NULL, $edit, t('Save configuration'));
+
+    // Verify that configuration has been updated.
+    $new_config_settings = config('config_test.settings');
+    $new_config_other = config('config_test.other');
+    $this->assertSame('settings: simple', $new_config_settings->get('simple'), (string) (int) $edit['simple']);
+    $this->assertSame('other: set.first', $new_config_other->get('set.first'), $edit['set[first]']);
+    $this->assertSame('other: set.second', $new_config_other->get('set.second'), $edit['set[second]']);
+    $this->assertSame('other: set.third', $new_config_other->get('set.third'), $edit['set[third]']);
+    $this->assertSame('settings: lost.child', $new_config_settings->get('lost.child'), $edit['lost_child']);
+    $this->assertNoVariables($new_config_settings->get());
+    $this->assertNoVariables($new_config_other->get());
+  }
+
+  /**
+   * Asserts that no variables exist for any keys contained in the given configuration data.
+   *
+   * @todo Technically obsolete with the entirely new and separate config_get_form().
+   */
+  function assertNoVariables(array $data) {
+    foreach ($data as $key => $value) {
+      $this->assertSame("Variable $key", variable_get($key), NULL);
+      if (is_array($value)) {
+        $this->assertNoVariables($value);
+      }
+    }
+  }
+
+  /**
+   * Asserts that two values are identical with a given name.
+   *
+   * @param string $name
+   *   The name or identifier to use as prefix in assertion messages.
+   * @param mixed $first
+   *   The first value to compare.
+   * @param mixed $second
+   *   The second value to compare.
+   * @param string $message
+   *   (optional) A custom assertion message to use. Should contain the function
+   *   arguments @name, @first, and @second as placeholders.
+   */
+  function assertSame($name, $first, $second, $message = NULL) {
+    if (!isset($message)) {
+      $message = '@name: @first is identical to @second.';
+    }
+    $this->assertIdentical($first, $second, format_string($message, array(
+      '@name' => $name,
+      '@first' => var_export($first, TRUE),
+      '@second' => var_export($second, TRUE),
+    )));
+  }
+}
+
   /**
    * Tests configuration overriding from settings.php.
    */
diff --git a/core/modules/config/tests/config_test/config/config_test.other.xml b/core/modules/config/tests/config_test/config/config_test.other.xml
new file mode 100644
index 0000000..8fbc291
--- /dev/null
+++ b/core/modules/config/tests/config_test/config/config_test.other.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<config>
+  <set>
+    <first>first</first>
+    <second>second</second>
+    <third>third</third>
+  </set>
+</config>
diff --git a/core/modules/config/tests/config_test/config/config_test.settings.xml b/core/modules/config/tests/config_test/config/config_test.settings.xml
new file mode 100644
index 0000000..5217e30
--- /dev/null
+++ b/core/modules/config/tests/config_test/config/config_test.settings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<config>
+  <simple>1</simple>
+  <lost>
+    <child>Individualist</child>
+  </lost>
+</config>
diff --git a/core/modules/config/tests/config_test/config_test.info b/core/modules/config/tests/config_test/config_test.info
new file mode 100644
index 0000000..ff3e589
--- /dev/null
+++ b/core/modules/config/tests/config_test/config_test.info
@@ -0,0 +1,7 @@
+name = Configuration test
+description = Helper module for configuration system tests.
+core = 8.x
+version = VERSION
+package = Core
+;hidden = TRUE
+dependencies[] = config
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
new file mode 100644
index 0000000..8d1be93
--- /dev/null
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * @file
+ * Helper module for configuration system tests.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function config_test_menu() {
+  $items['config_test/system_settings_form'] = array(
+    'title' => 'system_settings_form()',
+    'page callback' => 'config_get_form',
+    'page arguments' => array('config_test_system_settings_form'),
+    'access callback' => TRUE,
+  );
+  return $items;
+}
+
+/**
+ * Form constructor for system_settings_form() test.
+ */
+function config_test_system_settings_form($form, &$form_state) {
+  // @todo The #config_name properties are obsolete and only date back to an
+  //   earlier approach in which sun approached a smarter #preprocess logic that
+  //   would allow forms to set #config_name on the form structure and
+  //   #config_key on individual elements, which were then intelligently figured
+  //   out based on the #parents of all elements. The initial attempt failed,
+  //   but with the new #preprocess, it could be approached again.
+  $form['#config_name'] = 'config_test.settings';
+
+  $form['simple'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Simple',
+    '#config' => 'config_test.settings:simple',
+  );
+
+  // Typical case of module B extending settings form of module A.
+  // Note: The parent key 'set' is required for testing the advanced use-case.
+  $form['set'] = array(
+    '#config_name' => 'config_test.other',
+    '#tree' => TRUE,
+  );
+  $form['set']['first'] = array(
+    '#type' => 'textfield',
+    '#title' => 'First',
+    '#config' => 'config_test.other:set.first',
+  );
+  $form['set']['second'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Second',
+    '#config' => 'config_test.other:set.second',
+  );
+
+  // Complex merge: Module B defining another structure for itself, but outside
+  // of its #array_parents.
+  $form['parents_merge'] = array(
+    '#parents' => array('set'),
+    '#tree' => TRUE,
+  );
+  $form['parents_merge']['third'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Third',
+    '#config' => 'config_test.other:set.third',
+  );
+  // Advanced case of module C extending module B.
+  // Note: Part of module A's settings, because that's sufficient for testing.
+  $form['parents_merge']['lost_child'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Lost child',
+    '#tree' => FALSE,
+    '#config' => 'config_test.settings:lost.child',
+  );
+
+  return $form;
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index e685c16..4717f31 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2802,6 +2802,8 @@ function system_default_region($theme) {
  *
  * @see system_settings_form_submit()
  * @ingroup forms
+ *
+ * @todo Remove after full configuration system conversion.
  */
 function system_settings_form($form) {
   $form['actions']['#type'] = 'actions';
@@ -2823,6 +2825,8 @@ function system_settings_form($form) {
  *
  * If you want node type configure style handling of your checkboxes,
  * add an array_filter value to your form.
+ *
+ * @todo Remove after full configuration system conversion.
  */
 function system_settings_form_submit($form, &$form_state) {
   // Exclude unnecessary elements.
@@ -4024,6 +4028,8 @@ function theme_confirm_form($variables) {
  *   - form: A render element representing the form.
  *
  * @ingroup themeable
+ *
+ * @todo Remove after full configuration system conversion.
  */
 function theme_system_settings_form($variables) {
   return drupal_render_children($variables['form']);
