Vulnerability: memcached version prior to 1.4.37 contains an Integer Overflow vulnerability in items.c:item_free() that can result in data corruption and deadlocks due to items existing in hash table being reused from free list. This attack appear to be exploitable via network connectivity to the memcached service. This vulnerability appears to have been fixed in 1.4.37 and later.
Vulnerability ID: CVE-2018-1000127
CWE: CWE-190
Severity score: 2.9
Project: https://github.com/memcached/memcached
Published: 2018-03-13
File: memcached.c
Commit message: Don't overflow item refcount on get

Counts as a miss if the refcount is too high. ASCII multigets are the only
time refcounts can be held for so long.

doing a dirty read of refcount. is aligned.

trying to avoid adding an extra refcount branch for all calls of item_get due
to performance. might be able to move it in there after logging refactoring
simplifies some of the branches.
Commit author: dormando

Vulnerable version (before fix):
 static inline int make_ascii_get_suffix(char *suffix, item *it, bool return_cas)     return (p - suffix) + 2; }  /* ntokens is overwritten here... shrug.. */ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) {     char *key; static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens,                 return;             }              it = item_get(key, nkey, c, DO_UPDATE);             if (settings.detail_enabled) {                 stats_prefix_record_get(key, nkey, NULL != it);             }

Fixed version (after fix):
 static inline int make_ascii_get_suffix(char *suffix, item *it, bool return_cas)     return (p - suffix) + 2; }  #define IT_REFCOUNT_LIMIT 60000 static inline item* limited_get(char *key, size_t nkey, conn *c) {     item *it = item_get(key, nkey, c, DO_UPDATE);     if (it && it->refcount > IT_REFCOUNT_LIMIT) {         item_remove(it);         it = NULL;     }     return it; }  /* ntokens is overwritten here... shrug.. */ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) {     char *key; static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens,                 return;             }              it = limited_get(key, nkey, c);             if (settings.detail_enabled) {                 stats_prefix_record_get(key, nkey, NULL != it);             }

Patch:
patch: @@ -3249,6 +3249,16 @@ static inline int make_ascii_get_suffix(char *suffix, item *it, bool return_cas)
     return (p - suffix) + 2;
 }
 
+#define IT_REFCOUNT_LIMIT 60000
+static inline item* limited_get(char *key, size_t nkey, conn *c) {
+    item *it = item_get(key, nkey, c, DO_UPDATE);
+    if (it && it->refcount > IT_REFCOUNT_LIMIT) {
+        item_remove(it);
+        it = NULL;
+    }
+    return it;
+}
+
 /* ntokens is overwritten here... shrug.. */
 static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) {
     char *key;
@@ -3273,7 +3283,7 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens,
                 return;
             }
 
-            it = item_get(key, nkey, c, DO_UPDATE);
+            it = limited_get(key, nkey, c);
             if (settings.detail_enabled) {
                 stats_prefix_record_get(key, nkey, NULL != it);
             }
path: memcached.c