You're correct that any process can more or less send a message to any other process, but the difference is what guarantees the Erlang runtime provides around that idea.
For example, in Erlang, if I have processes A, B, and C, and B and C both send messages to A at the same time, the runtime guarantees that A processes the messages one at a time, in order, before moving on to the next message (there is some more detail here but it is not important to the point).
The runtime guarantees that from A's perspective, the messages from B and C cannot arrive "simultaneously" and trample on each other. The runtime also guarantees that A cannot process both messages at the same time. It processes the messages one at a time. All code in A is run linearly, single-threaded. The VM takes care of scheduling all of these single-threaded processes to run on the same hardware in parallel.
As other posters have pointed out, the runtime also guarantees that B and C cannot reach in and observe A's raw memory in an uncontrolled fashion (like you could in C, Java, etc.), so B and C cannot observe any intermediate states of A. The only way for B and C to get any information out of A is to send a message to A and then A can send a reply, if it wants. These replies are just normal messages, so they also obey all of the guarantees I've already described, so A will send the replies one at time, and they will end up in the mailboxes of B and C for their own processing.
Given all this (and more which I haven't gone into), Erlang doesn't have the concept of a data race where 2 or more threads are concurrently accessing the same memory region, as you might have in say, the C language (note that this is different than a logical race condition, which Erlang of course still can have).
I hope this is useful, you're asking good questions.
For example, in Erlang, if I have processes A, B, and C, and B and C both send messages to A at the same time, the runtime guarantees that A processes the messages one at a time, in order, before moving on to the next message (there is some more detail here but it is not important to the point).
The runtime guarantees that from A's perspective, the messages from B and C cannot arrive "simultaneously" and trample on each other. The runtime also guarantees that A cannot process both messages at the same time. It processes the messages one at a time. All code in A is run linearly, single-threaded. The VM takes care of scheduling all of these single-threaded processes to run on the same hardware in parallel.
As other posters have pointed out, the runtime also guarantees that B and C cannot reach in and observe A's raw memory in an uncontrolled fashion (like you could in C, Java, etc.), so B and C cannot observe any intermediate states of A. The only way for B and C to get any information out of A is to send a message to A and then A can send a reply, if it wants. These replies are just normal messages, so they also obey all of the guarantees I've already described, so A will send the replies one at time, and they will end up in the mailboxes of B and C for their own processing.
Given all this (and more which I haven't gone into), Erlang doesn't have the concept of a data race where 2 or more threads are concurrently accessing the same memory region, as you might have in say, the C language (note that this is different than a logical race condition, which Erlang of course still can have).
I hope this is useful, you're asking good questions.