summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--application/cache/.htaccess6
-rw-r--r--system/core/Loader.php72
-rw-r--r--system/database/DB_driver.php1
-rw-r--r--system/database/DB_query_builder.php14
-rw-r--r--system/database/drivers/mssql/mssql_driver.php2
-rw-r--r--system/database/drivers/mysqli/mysqli_result.php58
-rw-r--r--system/database/drivers/mysqli/mysqli_utility.php8
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php2
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php2
-rw-r--r--system/database/drivers/sqlsrv/sqlsrv_driver.php2
-rw-r--r--system/libraries/Cache/drivers/Cache_apc.php48
-rw-r--r--system/libraries/Image_lib.php2
-rw-r--r--system/libraries/Upload.php2
-rw-r--r--tests/codeigniter/core/Loader_test.php2
-rw-r--r--user_guide_src/source/changelog.rst31
-rw-r--r--user_guide_src/source/installation/downloads.rst3
-rw-r--r--user_guide_src/source/installation/upgrade_316.rst14
-rw-r--r--user_guide_src/source/installation/upgrade_317.rst14
-rw-r--r--user_guide_src/source/installation/upgrading.rst1
20 files changed, 196 insertions, 94 deletions
diff --git a/.gitignore b/.gitignore
index f6e8e993b..269044ea9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,11 +2,11 @@
application/cache/*
!application/cache/index.html
-!application/cache/.htaccess
application/logs/*
!application/logs/index.html
-!application/logs/.htaccess
+
+!application/*/.htaccess
composer.lock
@@ -28,4 +28,4 @@ user_guide_src/cilexer/pycilexer.egg-info/*
*.sublime-workspace
*.sublime-project
/tests/tests/
-/tests/results/ \ No newline at end of file
+/tests/results/
diff --git a/application/cache/.htaccess b/application/cache/.htaccess
deleted file mode 100644
index 6c63ed4c4..000000000
--- a/application/cache/.htaccess
+++ /dev/null
@@ -1,6 +0,0 @@
-<IfModule authz_core_module>
- Require all denied
-</IfModule>
-<IfModule !authz_core_module>
- Deny from all
-</IfModule> \ No newline at end of file
diff --git a/system/core/Loader.php b/system/core/Loader.php
index 6374558ad..580145231 100644
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -1027,6 +1027,26 @@ class CI_Loader {
return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
}
+ // Safety: Was the class already loaded by a previous call?
+ if (class_exists($class, FALSE))
+ {
+ $property = $object_name;
+ if (empty($property))
+ {
+ $property = strtolower($class);
+ isset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];
+ }
+
+ $CI =& get_instance();
+ if (isset($CI->$property))
+ {
+ log_message('debug', $class.' class already loaded. Second attempt ignored.');
+ return;
+ }
+
+ return $this->_ci_init_library($class, '', $params, $object_name);
+ }
+
// Let's search for the requested library file and load it.
foreach ($this->_ci_library_paths as $path)
{
@@ -1037,27 +1057,8 @@ class CI_Loader {
}
$filepath = $path.'libraries/'.$subdir.$class.'.php';
-
- // Safety: Was the class already loaded by a previous call?
- if (class_exists($class, FALSE))
- {
- // Before we deem this to be a duplicate request, let's see
- // if a custom object name is being supplied. If so, we'll
- // return a new instance of the object
- if ($object_name !== NULL)
- {
- $CI =& get_instance();
- if ( ! isset($CI->$object_name))
- {
- return $this->_ci_init_library($class, '', $params, $object_name);
- }
- }
-
- log_message('debug', $class.' class already loaded. Second attempt ignored.');
- return;
- }
// Does the file exist? No? Bummer...
- elseif ( ! file_exists($filepath))
+ if ( ! file_exists($filepath))
{
continue;
}
@@ -1102,16 +1103,17 @@ class CI_Loader {
$prefix = config_item('subclass_prefix');
}
- // Before we deem this to be a duplicate request, let's see
- // if a custom object name is being supplied. If so, we'll
- // return a new instance of the object
- if ($object_name !== NULL)
+ $property = $object_name;
+ if (empty($property))
{
- $CI =& get_instance();
- if ( ! isset($CI->$object_name))
- {
- return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
- }
+ $property = strtolower($library_name);
+ isset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];
+ }
+
+ $CI =& get_instance();
+ if ( ! isset($CI->$property))
+ {
+ return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
}
log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
@@ -1133,10 +1135,8 @@ class CI_Loader {
{
return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
}
- else
- {
- log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
- }
+
+ log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
}
}
@@ -1154,10 +1154,8 @@ class CI_Loader {
$prefix = config_item('subclass_prefix');
break;
}
- else
- {
- log_message('debug', $path.' exists, but does not declare '.$subclass);
- }
+
+ log_message('debug', $path.' exists, but does not declare '.$subclass);
}
}
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index e04525de3..54740c309 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -854,6 +854,7 @@ abstract class CI_DB_driver {
if ($this->_trans_begin())
{
+ $this->_trans_status = TRUE;
$this->_trans_depth++;
return TRUE;
}
diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php
index 3ff94be8e..e7b66ac80 100644
--- a/system/database/DB_query_builder.php
+++ b/system/database/DB_query_builder.php
@@ -1475,11 +1475,9 @@ abstract class CI_DB_query_builder extends CI_DB_driver {
// ORDER BY usage is often problematic here (most notably
// on Microsoft SQL Server) and ultimately unnecessary
// for selecting COUNT(*) ...
- if ( ! empty($this->qb_orderby))
- {
- $orderby = $this->qb_orderby;
- $this->qb_orderby = NULL;
- }
+ $qb_orderby = $this->qb_orderby;
+ $qb_cache_orderby = $this->qb_cache_orderby;
+ $this->qb_orderby = $this->qb_cache_orderby = NULL;
$result = ($this->qb_distinct === TRUE OR ! empty($this->qb_groupby) OR ! empty($this->qb_cache_groupby) OR $this->qb_limit OR $this->qb_offset)
? $this->query($this->_count_string.$this->protect_identifiers('numrows')."\nFROM (\n".$this->_compile_select()."\n) CI_count_all_results")
@@ -1489,10 +1487,10 @@ abstract class CI_DB_query_builder extends CI_DB_driver {
{
$this->_reset_select();
}
- // If we've previously reset the qb_orderby values, get them back
- elseif ( ! isset($this->qb_orderby))
+ else
{
- $this->qb_orderby = $orderby;
+ $this->qb_orderby = $qb_orderby;
+ $this->qb_cache_orderby = $qb_cache_orderby;
}
if ($result->num_rows() === 0)
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index e91a817b6..234be6786 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -441,7 +441,7 @@ class CI_DB_mssql_driver extends CI_DB {
$sql = trim(substr($sql, 0, strrpos($sql, $orderby)));
// Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results
- if (count($this->qb_select) === 0)
+ if (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)
{
$select = '*'; // Inevitable
}
diff --git a/system/database/drivers/mysqli/mysqli_result.php b/system/database/drivers/mysqli/mysqli_result.php
index 929c2b455..0b3d9c2b4 100644
--- a/system/database/drivers/mysqli/mysqli_result.php
+++ b/system/database/drivers/mysqli/mysqli_result.php
@@ -112,9 +112,9 @@ class CI_DB_mysqli_result extends CI_DB_result {
{
$retval[$i] = new stdClass();
$retval[$i]->name = $field_data[$i]->name;
- $retval[$i]->type = $field_data[$i]->type;
+ $retval[$i]->type = static::_get_field_type($field_data[$i]->type);
$retval[$i]->max_length = $field_data[$i]->max_length;
- $retval[$i]->primary_key = (int) ($field_data[$i]->flags & 2);
+ $retval[$i]->primary_key = (int) ($field_data[$i]->flags & MYSQLI_PRI_KEY_FLAG);
$retval[$i]->default = $field_data[$i]->def;
}
@@ -124,6 +124,60 @@ class CI_DB_mysqli_result extends CI_DB_result {
// --------------------------------------------------------------------
/**
+ * Get field type
+ *
+ * Extracts field type info from the bitflags returned by
+ * mysqli_result::fetch_fields()
+ *
+ * @used-by CI_DB_mysqli_result::field_data()
+ * @param int $flags
+ * @return string
+ */
+ private static function _get_field_type($flags)
+ {
+ static $map;
+ isset($map) OR $map = array(
+ MYSQLI_TYPE_DECIMAL => 'decimal',
+ MYSQLI_TYPE_BIT => 'bit',
+ MYSQLI_TYPE_TINY => 'tinyint',
+ MYSQLI_TYPE_SHORT => 'smallint',
+ MYSQLI_TYPE_INT24 => 'mediumint',
+ MYSQLI_TYPE_LONG => 'int',
+ MYSQLI_TYPE_LONGLONG => 'bigint',
+ MYSQLI_TYPE_FLOAT => 'float',
+ MYSQLI_TYPE_DOUBLE => 'double',
+ MYSQLI_TYPE_TIMESTAMP => 'timestamp',
+ MYSQLI_TYPE_DATE => 'date',
+ MYSQLI_TYPE_TIME => 'time',
+ MYSQLI_TYPE_DATETIME => 'datetime',
+ MYSQLI_TYPE_YEAR => 'year',
+ MYSQLI_TYPE_NEWDATE => 'date',
+ MYSQLI_TYPE_INTERVAL => 'interval',
+ MYSQLI_TYPE_ENUM => 'enum',
+ MYSQLI_TYPE_SET => 'set',
+ MYSQLI_TYPE_TINY_BLOB => 'tinyblob',
+ MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob',
+ MYSQLI_TYPE_BLOB => 'blob',
+ MYSQLI_TYPE_LONG_BLOB => 'longblob',
+ MYSQLI_TYPE_STRING => 'char',
+ MYSQLI_TYPE_VAR_STRING => 'varchar',
+ MYSQLI_TYPE_GEOMETRY => 'geometry'
+ );
+
+ foreach ($map as $flag => $name)
+ {
+ if ($flags & $flag)
+ {
+ return $name;
+ }
+ }
+
+ return $flags;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Free the result
*
* @return void
diff --git a/system/database/drivers/mysqli/mysqli_utility.php b/system/database/drivers/mysqli/mysqli_utility.php
index 4a3dad4d1..1699b611f 100644
--- a/system/database/drivers/mysqli/mysqli_utility.php
+++ b/system/database/drivers/mysqli/mysqli_utility.php
@@ -155,9 +155,11 @@ class CI_DB_mysqli_utility extends CI_DB_utility {
while ($field = $query->result_id->fetch_field())
{
// Most versions of MySQL store timestamp as a string
- $is_int[$i] = in_array(strtolower($field->type),
- array('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'),
- TRUE);
+ $is_int[$i] = ($field->type & MYSQLI_TYPE_TINY)
+ OR ($field->type & MYSQLI_TYPE_SHORT)
+ OR ($field->type & MYSQLI_TYPE_INT24)
+ OR ($field->type & MYSQLI_TYPE_LONG)
+ OR ($field->type & MYSQLI_TYPE_LONGLONG);
// Create a string of field names
$field_str .= $this->db->escape_identifiers($field->name).', ';
diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
index fbd279681..b9b86f784 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
@@ -284,7 +284,7 @@ class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver {
$sql = trim(substr($sql, 0, strrpos($sql, $orderby)));
// Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results
- if (count($this->qb_select) === 0)
+ if (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)
{
$select = '*'; // Inevitable
}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
index 07c429eec..a9fb4d14a 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
@@ -316,7 +316,7 @@ class CI_DB_pdo_sqlsrv_driver extends CI_DB_pdo_driver {
$sql = trim(substr($sql, 0, strrpos($sql, $orderby)));
// Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results
- if (count($this->qb_select) === 0)
+ if (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)
{
$select = '*'; // Inevitable
}
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index a43e2539a..4edcc7fb8 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -478,7 +478,7 @@ class CI_DB_sqlsrv_driver extends CI_DB {
$sql = trim(substr($sql, 0, strrpos($sql, $orderby)));
// Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results
- if (count($this->qb_select) === 0)
+ if (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)
{
$select = '*'; // Inevitable
}
diff --git a/system/libraries/Cache/drivers/Cache_apc.php b/system/libraries/Cache/drivers/Cache_apc.php
index f2b61adb1..c873eb640 100644
--- a/system/libraries/Cache/drivers/Cache_apc.php
+++ b/system/libraries/Cache/drivers/Cache_apc.php
@@ -80,14 +80,7 @@ class CI_Cache_apc extends CI_Driver {
$success = FALSE;
$data = apc_fetch($id, $success);
- if ($success === TRUE)
- {
- return is_array($data)
- ? unserialize($data[0])
- : $data;
- }
-
- return FALSE;
+ return ($success === TRUE) ? $data : FALSE;
}
// ------------------------------------------------------------------------
@@ -98,18 +91,12 @@ class CI_Cache_apc extends CI_Driver {
* @param string $id Cache ID
* @param mixed $data Data to store
* @param int $ttl Length of time (in seconds) to cache the data
- * @param bool $raw Whether to store the raw value
+ * @param bool $raw Whether to store the raw value (unused)
* @return bool TRUE on success, FALSE on failure
*/
public function save($id, $data, $ttl = 60, $raw = FALSE)
{
- $ttl = (int) $ttl;
-
- return apc_store(
- $id,
- ($raw === TRUE ? $data : array(serialize($data), time(), $ttl)),
- $ttl
- );
+ return apc_store($id, $data, (int) $ttl);
}
// ------------------------------------------------------------------------
@@ -188,21 +175,30 @@ class CI_Cache_apc extends CI_Driver {
*/
public function get_metadata($id)
{
- $success = FALSE;
- $stored = apc_fetch($id, $success);
-
- if ($success === FALSE OR count($stored) !== 3)
+ $cache_info = apc_cache_info('user', FALSE);
+ if (empty($cache_info) OR empty($cache_info['cache_list']))
{
return FALSE;
}
- list($data, $time, $ttl) = $stored;
+ foreach ($cache_info['cache_list'] as &$entry)
+ {
+ if ($entry['info'] !== $id)
+ {
+ continue;
+ }
+
+ $success = FALSE;
+ $metadata = array(
+ 'expire' => ($entry['ttl'] ? $entry['mtime'] + $entry['ttl'] : 0),
+ 'mtime' => $entry['ttl'],
+ 'data' => apc_fetch($id, $success)
+ );
+
+ return ($success === TRUE) ? $metadata : FALSE;
+ }
- return array(
- 'expire' => $time + $ttl,
- 'mtime' => $time,
- 'data' => unserialize($data)
- );
+ return FALSE;
}
// ------------------------------------------------------------------------
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 8b5a1adb0..60ed05766 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -972,7 +972,7 @@ class CI_Image_lib {
$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;
}
- $cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
+ $cmd = $this->library_path.$cmd_in.' '.escapeshellarg($this->full_src_path).' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
$retval = 1;
// exec() might be disabled
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index 3a1731e58..9e8389480 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -1310,7 +1310,7 @@ class CI_Upload {
}
}
- // Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
+ // Fall back to mime_content_type(), if available (still better than $_FILES[$field]['type'])
if (function_exists('mime_content_type'))
{
$this->file_type = @mime_content_type($file['tmp_name']);
diff --git a/tests/codeigniter/core/Loader_test.php b/tests/codeigniter/core/Loader_test.php
index 3b6a7e16c..df698f30c 100644
--- a/tests/codeigniter/core/Loader_test.php
+++ b/tests/codeigniter/core/Loader_test.php
@@ -99,7 +99,7 @@ class Loader_test extends CI_TestCase {
// Test reloading
unset($this->ci_obj->$name);
$this->assertInstanceOf('CI_Loader', $this->load->library($lib));
- $this->assertObjectNotHasAttribute($name, $this->ci_obj);
+ $this->assertObjectHasAttribute($name, $this->ci_obj);
// Create baseless library
$name = 'ext_baseless_lib';
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 4b0a6e56a..a1321fe94 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -114,12 +114,41 @@ Release Date: Not Released
- Removed the second (out of three) parameter from the :php:func:`form_upload()` function (it was never used).
-Version 3.1.6
+Version 3.1.7
=============
Release Date: Not Released
+Version 3.1.6
+=============
+
+Release Date: Sep 25, 2017
+
+- **Security**
+
+ - Fixed a potential object injection in :doc:`Cache Library <libraries/caching>` 'apc' driver when ``save()`` is used with ``$raw = TRUE`` (thanks to Tomas Bortoli).
+
+- General Changes
+
+ - Deprecated :doc:`Cache Library Library <libraries/caching>` driver 'apc'.
+ - Updated the :doc:`Session Library <libraries/sessions>` 'redis', 'memcached' drivers to reduce the potential of a locking race conditions.
+
+
+Bug fixes for 3.1.6
+-------------------
+
+- Fixed a bug (#5164) - :doc:`Loader Library <libraries/loader>` method ``library()`` ignored requests to load libraries previously assigned to super-object properties named differently than the library name.
+- Fixed a bug (#5168) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` produced erroneous queries on Microsoft SQL Server when ``ORDER BY`` clauses are cached.
+- Fixed a bug (#5128) - :doc:`Profiler <general/profiling>` didn't wrap ``$_SESSION`` and configuration arrays in ``<pre>`` tags.
+- Fixed a bug (#5183) - :doc:`Database Library <database/index>` method ``is_write_type()`` didn't return TRUE for ``MERGE`` statements.
+- Fixed a bug where :doc:`Image Manipulation Library <libraries/image_lib>` didn't escape image source paths passed to NetPBM as shell arguments.
+- Fixed a bug (#5236) - :doc:`Query Builder <database/query_builder>` methods ``limit()``, ``offset()`` break SQL Server 2005, 2008 queries with ``"<tablename>".*`` in the ``SELECT`` clause.
+- Fixed a bug (#5243) - :doc:`Database Library <database/index>` method ``version()`` didn't work with the 'pdo/dblib' driver.
+- Fixed a bug (#5246) - :doc:`Database transactions <database/transactions>` status wasn't reset unless ``trans_complete()`` was called.
+- Fixed a bug (#5260) - :doc:`Database Utilities <database/utilities>` method ``backup()`` generated incorrect ``INSERT`` statements with the 'mysqli' driver.
+- Fixed a bug where :doc:`Database Results <database/results>` method ``field_data()`` didn't parse field types with the 'mysqli' driver.
+
Version 3.1.5
=============
diff --git a/user_guide_src/source/installation/downloads.rst b/user_guide_src/source/installation/downloads.rst
index fb9222df9..337ea35d5 100644
--- a/user_guide_src/source/installation/downloads.rst
+++ b/user_guide_src/source/installation/downloads.rst
@@ -3,7 +3,8 @@ Downloading CodeIgniter
#######################
- `CodeIgniter v3.2.0-dev (Current version) <https://codeload.github.com/bcit-ci/CodeIgniter/zip/develop>`_
-- `CodeIgniter v3.1.6-dev <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1-stable>`_
+- `CodeIgniter v3.1.7-dev <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1-stable>`_
+- `CodeIgniter v3.1.6 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.6>`_
- `CodeIgniter v3.1.5 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.5>`_
- `CodeIgniter v3.1.4 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.4>`_
- `CodeIgniter v3.1.3 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.3>`_
diff --git a/user_guide_src/source/installation/upgrade_316.rst b/user_guide_src/source/installation/upgrade_316.rst
index e382c20ab..1d86b5720 100644
--- a/user_guide_src/source/installation/upgrade_316.rst
+++ b/user_guide_src/source/installation/upgrade_316.rst
@@ -12,3 +12,17 @@ Replace all files and directories in your *system/* directory.
.. note:: If you have any custom developed files in these directories,
please make copies of them first.
+
+Step 2: Remove usage of the APC Cache driver (deprecation)
+==========================================================
+
+The :doc:`Cache Library <../libraries/caching>` APC driver is now
+deprecated, as the APC extension is effectively dead, as explained in its
+`PHP Manual page <https://secure.php.net/manual/en/intro.apc.php>`_.
+
+If your application happens to be using it, you can switch to another
+cache driver, as APC support will be removed in a future CodeIgniter
+version.
+
+.. note:: The driver is still available, but you're strongly encouraged
+ to remove its usage sooner rather than later.
diff --git a/user_guide_src/source/installation/upgrade_317.rst b/user_guide_src/source/installation/upgrade_317.rst
new file mode 100644
index 000000000..93c37b687
--- /dev/null
+++ b/user_guide_src/source/installation/upgrade_317.rst
@@ -0,0 +1,14 @@
+#############################
+Upgrading from 3.1.6 to 3.1.7
+#############################
+
+Before performing an update you should take your site offline by
+replacing the index.php file with a static one.
+
+Step 1: Update your CodeIgniter files
+=====================================
+
+Replace all files and directories in your *system/* directory.
+
+.. note:: If you have any custom developed files in these directories,
+ please make copies of them first.
diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst
index 3383796d0..ce1d8a9aa 100644
--- a/user_guide_src/source/installation/upgrading.rst
+++ b/user_guide_src/source/installation/upgrading.rst
@@ -9,6 +9,7 @@ upgrading from.
:titlesonly:
Upgrading from 3.1.3+ to 3.2.x <upgrade_320>
+ Upgrading from 3.1.6 to 3.1.7 <upgrade_317>
Upgrading from 3.1.5 to 3.1.6 <upgrade_316>
Upgrading from 3.1.4 to 3.1.5 <upgrade_315>
Upgrading from 3.1.3 to 3.1.4 <upgrade_314>