On real systems, sbrk returns page-granularity allocations. I'm not sure if the SPIM simulator does (the meagre online docs imply it will return any byte-oriented granularity).
Generally, the sbrk system call just sets the "end-of-heap" pointer. All the underlying OS knows is the beginning of the heap (where sbrk started at the beginning of the program), and the current end-of-heap pointer. All the memory in that bound is considered (from the OS point of view) heap memory in use by the program.
(Note, in your case I believe the SPIM simulator takes an integer to bump the pointer by, so implicitly all the memory is contiguous and I think the break is always increasing?)
The "malloc" API that is generally built on sbrk provides more than a simple contiguous, growing memory region. A good malloc library generally lets you mark a region of memory as "free" (so it can be used to satisfy a subsequent malloc call). Note that OS generally doesn't know about this "free". Malloc keeps track of the memory that is free or not. In general malloc cannot return this arbitrary region of the heap to the OS, because the heap is a single contiguous region from the OS point of view.
Real malloc implementations have to deal with the mis-match between allocation requests and page-size (normal sbrk only returns multiples of page-aligned, page-sized allocations). Your simulator case doesn't have this problem because sbrk is fine-grained.
Note that tracking which memory is in use or not requires memory, so malloc has some overhead. Some implementations are designed to store most of this bookkeeping in the "free" memory (reducing the apparent cost to the client). There are lots of other strategies for matching malloc to sbrk.... (In your case mapping malloc to sbrk, and making free a no-op will 'work' as long as you don't allocate too much memory ...)
Here's an overview of how malloc and sbrk relate that draws some ASCII art I don't have the patience to transcribe: http://web.eecs.utk.edu/~huangj/cs360/360/notes/Malloc1/lecture.html