CVE-2012-3371: Improper Input Validation (CWE-20)
value: The Nova scheduler in OpenStack Compute (Nova) Folsom (2012.2) and Essex (2012.1), when DifferentHostFilter or SameHostFilter is enabled, allows remote authenticated users to cause a denial of service (excessive database lookup calls and server hang) via a request with many repeated IDs in the os:scheduler_hints section.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Published: 2012-07-17T21:55Z
Repository: https://github.com/openstack/nova
Language: Python
Files changed: nova/scheduler/filters/affinity_filter.py
Commit date: 2012-07-11 10:23:58 -0400
Commit message: Use compute_api.get_all in affinity filters.

Updates the affinity filters so they make a single compute API
call to lookup instance host information rather than single
lookups for each UUID.

This resolves a potential performance issue which can cause a
scheduler to hang while processing requests which contain large numbers
of UUID's in the scheduler_hints.

Fixes LP Bug #1017795.

Change-Id: I30f434faf109058573ee41c4a6abce2e48939e8d

Diff: diff --git a/nova/scheduler/filters/affinity_filter.py b/nova/scheduler/filters/affinity_filter.py
index e2a38b1e63..03bf0dd6ee 100644
--- a/nova/scheduler/filters/affinity_filter.py
+++ b/nova/scheduler/filters/affinity_filter.py
@@ -23,12 +23,15 @@ from nova.scheduler import filters
 
 class AffinityFilter(filters.BaseHostFilter):
     def __init__(self):
         self.compute_api = compute.API()
 
-    def _affinity_host(self, context, instance_id):
-        return self.compute_api.get(context, instance_id)['host']
+    def _all_hosts(self, context):
+        all_hosts = {}
+        for instance in self.compute_api.get_all(context):
+            all_hosts[instance['uuid']] = instance['host']
+        return all_hosts
 
 
 class DifferentHostFilter(AffinityFilter):
     '''Schedule the instance on a different host from a set of instances.'''
 
@@ -39,12 +42,13 @@ class DifferentHostFilter(AffinityFilter):
 
         affinity_uuids = scheduler_hints.get('different_host', [])
         if isinstance(affinity_uuids, basestring):
             affinity_uuids = [affinity_uuids]
         if affinity_uuids:
+            all_hosts = self._all_hosts(context)
             return not any([i for i in affinity_uuids
-                              if self._affinity_host(context, i) == me])
+                              if all_hosts.get(i) == me])
         # With no different_host key
         return True
 
 
 class SameHostFilter(AffinityFilter):
@@ -59,13 +63,14 @@ class SameHostFilter(AffinityFilter):
 
         affinity_uuids = scheduler_hints.get('same_host', [])
         if isinstance(affinity_uuids, basestring):
             affinity_uuids = [affinity_uuids]
         if affinity_uuids:
+            all_hosts = self._all_hosts(context)
             return any([i for i
                           in affinity_uuids
-                          if self._affinity_host(context, i) == me])
+                          if all_hosts.get(i) == me])
         # With no same_host key
         return True
 
 
 class SimpleCIDRAffinityFilter(AffinityFilter):

Vulnerable code:     def _affinity_host(self, context, instance_id):
        return self.compute_api.get(context, instance_id)['host']
                              if self._affinity_host(context, i) == me])
                          if self._affinity_host(context, i) == me])
Fixed code:     def _all_hosts(self, context):
        all_hosts = {}
        for instance in self.compute_api.get_all(context):
            all_hosts[instance['uuid']] = instance['host']
        return all_hosts
            all_hosts = self._all_hosts(context)
                              if all_hosts.get(i) == me])
            all_hosts = self._all_hosts(context)
                          if all_hosts.get(i) == me])