Skip to content

[Bug] Passing a negative number for 'n' to 'get_nns_by_item' or 'get_nns_by_vector' causes incorrect results and integer overflow #700

Description

@saitejabandaru-in

When querying an Annoy index using the Python API, if a negative integer (e.g. n = -1) is passed as the n argument to get_nns_by_item or get_nns_by_vector, the function returns completely incorrect results or empty lists [] rather than throwing a Python exception.

Reproduction

from annoy import AnnoyIndex

t = AnnoyIndex(10, 'angular')
for i in range(10):
    v = [1 if j == i else 0 for j in range(10)]
    t.add_item(i, v)
t.build(10)

# Expected: [0], but gets completely unrelated item or empty list
print(t.get_nns_by_item(0, -1))
print(t.get_nns_by_item(0, -2))

Root Cause

In annoymodule.cc, n is parsed as a signed int32_t:

int32_t item, n, search_k=-1, include_distances=0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", (char**)kwlist, &item, &n, &search_k, &include_distances))

However, in annoylib.h, the corresponding argument is an unsigned size_t n:

void get_nns_by_item(S item, size_t n, int search_k, vector<S>* result, vector<T>* distances) const

When n = -1 is passed, it is sign-extended to SIZE_MAX (e.g., 18446744073709551615).

This leads to cascading issues in _get_all_nns:

  1. search_k Overflow: search_k = n * _roots.size(); overflows. Because search_k is int, it silently wraps around to a negative number.
  2. Unbounded Search Condition: while (nns.size() < (size_t)search_k && !q.empty()) converts the negative search_k back to SIZE_MAX, causing the search to attempt exploring the entire tree unbounded.
  3. Garbage Results: size_t p = n < m ? n : m; evaluates to m (because SIZE_MAX < m is false), causing all explored items to be returned. Due to unbounded exploration boundaries, this often yields completely random incorrect items or [].

Suggested Fix

Add a parameter constraint check in annoymodule.cc to ensure n >= 0 before making calls to the C++ core:

if (n < 0) {
    PyErr_SetString(PyExc_ValueError, "n must be a non-negative integer");
    return NULL;
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions