- Do all threads of a process work in the same virtual address space?
Yes. Each thread has its own stack, but all the memory associated with the process is in the same virtual address space. If a function in one thread had a pointer to a stack-allocated variable in another thread, that function could read from/write to that variable.
- When we say threads can still access stack of other threads, dos that mean for example if our stack starts from 0x00 to 0xff then one thread might work from 0x00 to 0x0f and the other one works for 0x10 to 0xff?
Like I said in my response to your first question, each thread gets its own stack. In the abstract, that means that one thread's stack runs from A to B, and a different thread's stack runs from C to D, where A < B < C < D (i.e., the A-B range doesn't overlap with the C-D range).
- Is the stack the only part in virtual memory that is unique per thread in Linux?
The uniqueness of the stacks isn't related to virtual memory. The stack (really "activation record stack") is used to store information about running functions (both the currently running function, and all of the calling functions). If there are multiple threads, then there are multiple functions (and calling functions). If the threads were to share a stack where the activation records were interleaved, it's be almost impossible to know to which calling function to return when a function finishes.