Vulnerability: 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.
Vulnerability ID: CVE-2012-3371
CWE: CWE-20
Severity score: 2.9
Project: https://github.com/openstack/nova
Published: 2012-07-17
File: nova/scheduler/filters/affinity_filter.py
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
Commit author: Dan Prince

Vulnerable version (before fix):
 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']   class DifferentHostFilter(AffinityFilter): def host_passes(self, host_state, filter_properties):         if isinstance(affinity_uuids, basestring):             affinity_uuids = [affinity_uuids]         if affinity_uuids:             return not any([i for i in affinity_uuids                               if self._affinity_host(context, i) == me])         # With no different_host key         return True  def host_passes(self, host_state, filter_properties):         if isinstance(affinity_uuids, basestring):             affinity_uuids = [affinity_uuids]         if affinity_uuids:             return any([i for i                           in affinity_uuids                           if self._affinity_host(context, i) == me])         # With no same_host key         return True 

Fixed version (after fix):
 class AffinityFilter(filters.BaseHostFilter):     def __init__(self):         self.compute_api = compute.API()      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): def host_passes(self, host_state, filter_properties):         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 all_hosts.get(i) == me])         # With no different_host key         return True  def host_passes(self, host_state, filter_properties):         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 all_hosts.get(i) == me])         # With no same_host key         return True 

Patch:
patch: @@ -25,8 +25,11 @@ 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):
@@ -41,8 +44,9 @@ def host_passes(self, host_state, filter_properties):
         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
 
@@ -61,9 +65,10 @@ def host_passes(self, host_state, filter_properties):
         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
path: nova/scheduler/filters/affinity_filter.py