Your understanding of DI is pretty good. And yes, Symfony Controller does implement ContainerAwareInterface, and as you said, has a service locator role. But service locator isn't an anti-pattern. Each pattern has it's proper and improper uses.
Furthermore, Symfony doesn't enforce you in any way to use it's Controller. Your Controller can be a service. Hell, it can even be a function!
Here is one of the reasons why Controllers are implemented as service locators: Performance.
Let's drop car analogy and focus on real case that you'll encounter in 99% of projects: you need CRUD for a resource. Let's say you're building a Todo app and you need a RESTfulish controller to handle CRUD operations for Task Resource.
The least you need to have is a way to read all tasks and a way to add a new task, for that you need two actions: index (commonly named list too), and store (commonly named create too).
Common flow in Symfony would be this, in pseudo code:
indexAction -> getDoctrine -> getTaskRepository -> getAllTasks storeAction -> getFormFactory -> createForm -> bindRequestDataToForm -> getDoctrine -> saveData
If Controller was a service locator
Index Action
When index action is executed, only service that will be resolved from the container will be ManagerRegistry (in this case Doctrine service). We will then ask it to give us task repository, and we'll do our operation with it.
Store Action
When store action is executed, we will do a bit more work: ask container to give us FormFactory, do some operations with it, and then ask it to give us Doctrine and do some operations with it too.
So summary: when index action is executed, only one service has to be constructed by service container, when update is executed, two will have to be constructed.
If Controller was a regular service
Let's see what our Controller needs. From the section above, we see that it needs FormFactory and Doctrine.
Now, when you just want to call index action to read all tasks from data storage, your controller will have to get instantiated by container. Before it can be instantiated, container needs to instantiate it's dependencies: FormFactory and Doctrine. Then instantiate controller while injecting those two into it.
So, you are calling index action which doesn't need FormFactory at all, but you still have overhead of creating it because it is needed for an action that will not be called at all in that request.
Lazy Services
To reduce this overhead, there is a thing called lazy service. It works by actually injecting a Proxy of your service into the controller. So, as far as controller is concerned, it got FormFactory. What it doesn't know is that isn't real FormFactory but instead a fake object which will delegate calls to real FormFactory code when you call some method on it.
Wrapping it up
Controller doesn't have to be a service locator, but can be. Making it a service locator can be a bit more performant and easier to bootstrap, but hides dependencies. Furthermore, it's a bit harder to test, since you'll need to mock dependency container. Whether you want to make your controllers services, functions or service locators is your choice and Symfony won't enforce you to use any of those ways.
In my experience, extending default Symfony Controller and having controllers be service locators is just fine as long as you don't write your business logic in them, but instead delegate all that work to services, which you get from container. That way, it's really unlikely that you'll have bugs in controller code (as methods will usually consist of 2-3 lines of code) and can get away without testing it.